Objectives

  • Define a decision tree
  • Identify the steps to estimating a decision tree
  • Demonstrate how to estimate a decision tree for regression and classification problems
  • Define and estimate bagging models
  • Define and estimate random forest models
  • Define and estimate boosting models
library(tidyverse)
library(forcats)
library(broom)
library(modelr)
library(tree)
library(randomForest)
library(stringr)
library(ISLR)
library(gridExtra)
library(grid)
library(titanic)
library(rcfss)
library(pROC)
library(gbm)
# to get the tree graphs with the labels and values, use the forked
# version of ggdendro
# devtools::install_github("bensoltoff/ggdendro")
library(ggdendro)

options(digits = 3)
set.seed(1234)
theme_set(theme_minimal())
err.rate.rf <- function(model, data) {
  data <- as_tibble(data)
  response <- as.character(model$terms[[2]])
  
  pred <- predict(model, newdata = data, type = "response")
  actual <- data[[response]]
  
  return(mean(pred != actual, na.rm = TRUE))
}

Decision trees

Decision trees are intuitive concepts for making decisions. They are also useful methods for regression and classification. They work by splitting the observations into a number of regions, and predictions are made based on the mean or mode of the training observations in that region.

Regression trees

Single predictor

Let’s first consider a basic linear regression model of the relationship between horsepower and highway mileage from the Auto dataset.

# add 95% confidence intervals to fitted values from augment()
add_ci <- function(df_augment) {
  df_augment %>%
    mutate(.fitted.low = .fitted - 1.96 * .se.fit,
           .fitted.high = .fitted + 1.96 * .se.fit)
}

# draw 95% confidence interval plot using results of add_ci()
plot_ci <- function(df_ci, x){
  ggplot(df_ci, aes_string(x, ".fitted")) +
  geom_line() +
  geom_line(aes(y = .fitted.low), linetype = 2) +
  geom_line(aes(y = .fitted.high), linetype = 2)
}

auto_lm <- glm(mpg ~ horsepower, data = Auto)

augment(auto_lm, newdata = data_grid(Auto, horsepower)) %>%
  add_ci() %>%
  plot_ci("horsepower") +
  geom_point(data = Auto, aes(y = mpg), alpha = .2) +
  labs(title = "Linear model of highway mileage",
       x = "Horsepower",
       y = "Highway mileage")

As we recall, a strictly linear model is a poor fit for the data since the relationship actually appears to be quadratic. But unless we relax our linear assumption, this is the best OLS model we can estimate.

Let’s compare this instead to a decision tree using horsepower to predict highway mileage. Decision trees work through a process of stratification:

  1. Divide the predictor space (\(X_1, X_2, \dots, X_p\)) into \(J\) distinct and non-overlapping regions \(R_1, R_2, \dots, R_J\).
  2. For every observation in region \(R_j\), we make the same prediction which is the mean of the response variable \(Y\) for all observations in \(R_j\).

This process is iterative: during the first iteration, we segment the predictor space \(X\) into two regions \(R_1, R_2\). In the context of a decision tree with a single predictor, that process results in decision trees like the following:

# hackish function to get line segment coordinates for ggplot
partition.tree.data <- function (tree, label = "yval", add = FALSE, ordvars, ...) 
{
  ptXlines <- function(x, v, xrange, xcoord = NULL, ycoord = NULL, 
                       tvar, i = 1L) {
    if (v[i] == "<leaf>") {
      y1 <- (xrange[1L] + xrange[3L])/2
      y2 <- (xrange[2L] + xrange[4L])/2
      return(list(xcoord = xcoord, ycoord = c(ycoord, y1, 
                                              y2), i = i))
    }
    if (v[i] == tvar[1L]) {
      xcoord <- c(xcoord, x[i], xrange[2L], x[i], xrange[4L])
      xr <- xrange
      xr[3L] <- x[i]
      ll2 <- Recall(x, v, xr, xcoord, ycoord, tvar, i + 
                      1L)
      xr <- xrange
      xr[1L] <- x[i]
      return(Recall(x, v, xr, ll2$xcoord, ll2$ycoord, tvar, 
                    ll2$i + 1L))
    }
    else if (v[i] == tvar[2L]) {
      xcoord <- c(xcoord, xrange[1L], x[i], xrange[3L], 
                  x[i])
      xr <- xrange
      xr[4L] <- x[i]
      ll2 <- Recall(x, v, xr, xcoord, ycoord, tvar, i + 
                      1L)
      xr <- xrange
      xr[2L] <- x[i]
      return(Recall(x, v, xr, ll2$xcoord, ll2$ycoord, tvar, 
                    ll2$i + 1L))
    }
    else stop("wrong variable numbers in tree.")
  }
  if (inherits(tree, "singlenode")) 
    stop("cannot plot singlenode tree")
  if (!inherits(tree, "tree")) 
    stop("not legitimate tree")
  frame <- tree$frame
  leaves <- frame$var == "<leaf>"
  var <- unique(as.character(frame$var[!leaves]))
  if (length(var) > 2L || length(var) < 1L) 
    stop("tree can only have one or two predictors")
  nlevels <- sapply(attr(tree, "xlevels"), length)
  if (any(nlevels[var] > 0L)) 
    stop("tree can only have continuous predictors")
  x <- rep(NA, length(leaves))
  x[!leaves] <- as.double(substring(frame$splits[!leaves, "cutleft"], 
                                    2L, 100L))
  m <- model.frame(tree)
  if (length(var) == 1L) {
    x <- sort(c(range(m[[var]]), x[!leaves]))
    if (is.null(attr(tree, "ylevels"))) 
      y <- frame$yval[leaves]
    else y <- frame$yprob[, 1L]
    y <- c(y, y[length(y)])
    if (add) {
      # lines(x, y, type = "s", ...)
    }
    else {
      a <- attributes(attr(m, "terms"))
      yvar <- as.character(a$variables[1 + a$response])
      xo <- m[[yvar]]
      if (is.factor(xo)) 
        ylim <- c(0, 1)
      else ylim <- range(xo)
      # plot(x, y, ylab = yvar, xlab = var, type = "s", ylim = ylim,
      #      xaxs = "i", ...)
    }
    data_frame(x = x, y = y)
  }
  else {
    if (!missing(ordvars)) {
      ind <- match(var, ordvars)
      if (any(is.na(ind))) 
        stop("unmatched names in vars")
      var <- ordvars[sort(ind)]
    }
    lab <- frame$yval[leaves]
    if (is.null(frame$yprob)) 
      lab <- format(signif(lab, 3L))
    else if (match(label, attr(tree, "ylevels"), nomatch = 0L)) 
      lab <- format(signif(frame$yprob[leaves, label], 
                           3L))
    rx <- range(m[[var[1L]]])
    rx <- rx + c(-0.025, 0.025) * diff(rx)
    rz <- range(m[[var[2L]]])
    rz <- rz + c(-0.025, 0.025) * diff(rz)
    xrange <- c(rx, rz)[c(1, 3, 2, 4)]
    xcoord <- NULL
    ycoord <- NULL
    xy <- ptXlines(x, frame$var, xrange, xcoord, ycoord, 
                   var)
    xx <- matrix(xy$xcoord, nrow = 4L)
    yy <- matrix(xy$ycoord, nrow = 2L)

    return(list(data_frame(xmin = xx[1L,],
                           ymin = xx[2L,],
                           xmax = xx[3L,],
                           ymax = xx[4L,]),
                data_frame(x = yy[1L,],
                           y = yy[2L,],
                           label = lab)))
    # if (!add) 
    #   plot(rx, rz, xlab = var[1L], ylab = var[2L], type = "n", 
    #        xaxs = "i", yaxs = "i", ...)
    # segments(xx[1L, ], xx[2L, ], xx[3L, ], xx[4L, ])
    # text(yy[1L, ], yy[2L, ], as.character(lab), ...)
  }
}
# estimate model
auto_tree <- tree(mpg ~ horsepower, data = Auto,
     control = tree.control(nobs = nrow(Auto),
                            mindev = 0))

mod <- prune.tree(auto_tree, best = 2)

# plot tree
tree_data <- dendro_data(mod)
ptree <- ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro()

# plot region space
preg <- ggplot(Auto, aes(horsepower, mpg)) +
  geom_point(alpha = .2) +
  geom_step(data = partition.tree.data(mod), aes(x, y), size = 1.5) +
  geom_vline(data = partition.tree.data(mod), aes(xintercept = x), linetype = 2) +
  coord_cartesian(xlim = c(min(Auto$horsepower), max(Auto$horsepower)),
                  ylim = c(min(Auto$mpg), max(Auto$mpg)),
                  expand = FALSE) +
  theme(panel.border = element_rect(fill = NA, size = 1))

# display plots side by side
grid.arrange(ptree, preg, ncol = 2,
             top = textGrob(str_c("Terminal Nodes = ", ceiling(length(mod$frame$yval) / 2)),
                            gp = gpar(fontsize = 20)))

On the left is the decision tree after the first iteration, and on the right is the decision tree estimation of the relationship between horsepower and highway mileage. The tree consists of three different components:

  • Each outcome is a terminal node or a leaf
  • Splits occur at internal nodes
  • The segments connecting each node are called branches

This model has two terminal nodes (29.038 and 17.854), one internal node (horsepower \(<93.5\)), and two branches. For observations with horsepower \(<93.5\), the model estimates highway mileage of 29.038. For observations with horsepower \(>93.5\), the model estimates highway mileage of 17.854. The resulting relationship “curve” (see right) looks like a step function. Each segment of the function is the mean() of the observations inside that region.

If we proceed to the next iteration, the decision tree segments \(R_1\) further.

mod <- prune.tree(auto_tree, best = 3)

# plot tree
tree_data <- dendro_data(mod)
ptree <- ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro()

# plot region space
preg <- ggplot(Auto, aes(horsepower, mpg)) +
  geom_point(alpha = .2) +
  geom_step(data = partition.tree.data(mod), aes(x, y), size = 1.5) +
  geom_vline(data = partition.tree.data(mod), aes(xintercept = x), linetype = 2) +
  coord_cartesian(xlim = c(min(Auto$horsepower), max(Auto$horsepower)),
                  ylim = c(min(Auto$mpg), max(Auto$mpg)),
                  expand = FALSE) +
  theme(panel.border = element_rect(fill = NA, size = 1))

# display plots side by side
grid.arrange(ptree, preg, ncol = 2,
             top = textGrob(str_c("Terminal Nodes = ", ceiling(length(mod$frame$yval) / 2)),
                            gp = gpar(fontsize = 20)))

Now there are three terminal nodes (33.666, 26.409, 17.854), two internal nodes (horsepower \(<93.5\) and horsepower \(<70.5\)), and three branches. Interpreting the decision tree is still relatively intuitive:

  • If horsepower \(>93.5\), then the model estimates highway mileage to be 17.854.
  • If horsepower \(<93.5\), then we proceed down the left branch to the next internal node.
    • If horsepower \(<70.5\), then the model estimates highway mileage to be 33.666.
    • If horsepower \(>70.5\), then the model estimates highway mileage to be 26.409.

If we continued the iterative process many many times, we’d get a decision tree that looks like this:

mod <- auto_tree

# plot tree
tree_data <- dendro_data(mod)
ptree <- ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro()

# plot region space
preg <- ggplot(Auto, aes(horsepower, mpg)) +
  geom_point(alpha = .2) +
  geom_step(data = partition.tree.data(mod), aes(x, y), size = 1.5) +
  geom_smooth(data = partition.tree.data(mod), aes(x, y), se = FALSE) +
  coord_cartesian(xlim = c(min(Auto$horsepower), max(Auto$horsepower)),
                  ylim = c(min(Auto$mpg), max(Auto$mpg)),
                  expand = FALSE) +
  theme(panel.border = element_rect(fill = NA, size = 1))

# display plots side by side
grid.arrange(ptree, preg, ncol = 2,
             top = textGrob(str_c("Terminal Nodes = ", ceiling(length(mod$frame$yval) / 2)),
                            gp = gpar(fontsize = 20)))

There are 77 nodes (internal and terminal) in this decision tree, with 39 different regions and 39 different predicted values depending on the observation’s value for horsepower. Notice though that the step function actually looks similar to a quadratic smoothing line, matching our expectations of the relationship. In fact, compared to the linear model (23.944) the decision tree generates a far lower training MSE (14.498).1

Multiple predictors

With just a single predictor, the regions are a function of that one predictor. If we add a second predictor (say, vehicle weight), the regions become a function of both predictors and can be visualized as grids or boxes.

auto_tree <- tree(mpg ~ horsepower + weight, data = Auto,
     control = tree.control(nobs = nrow(Auto),
                            mindev = 0))

mod <- prune.tree(auto_tree, best = 3)

# plot tree
tree_data <- dendro_data(mod)
ptree <- ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro()

# plot region space
preg <- ggplot(Auto, aes(weight, horsepower)) +
  geom_point(alpha = .2) +
  geom_segment(data = partition.tree.data(mod)[[1]],
               aes(x = xmin, xend = xmax, y = ymin, yend = ymax)) +
  geom_text(data = partition.tree.data(mod)[[2]],
            aes(x = x, y = y, label = label)) +
  coord_cartesian(xlim = c(min(Auto$weight), max(Auto$weight)),
                  ylim = c(min(Auto$horsepower), max(Auto$horsepower)),
                  expand = FALSE) +
  theme(panel.border = element_rect(fill = NA, size = 1))

# display plots side by side
grid.arrange(ptree, preg, ncol = 2,
             top = textGrob(str_c("Terminal Nodes = ", ceiling(length(mod$frame$yval) / 2)),
                            gp = gpar(fontsize = 20)))

  • If weight \(>2764.5\), then the model estimates highway mileage to be approximately 17.769.
  • If weight \(<2764.5\), then we proceed down the left branch to the next internal node.
    • If horsepower \(<70.5\), then the model estimates highway mileage to be 33.68.
    • If horsepower \(>70.5\), then the model estimates highway mileage to be 27.011.

We can continue to build the tree up by adding additional nodes:

for(i in c(4:10, 20, 50)){
  mod <- prune.tree(auto_tree, best = i)
  
  # plot tree
  tree_data <- dendro_data(mod)
  ptree <- ggplot(segment(tree_data)) +
    geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
                 alpha = 0.5) +
    geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
    geom_text(data = leaf_label(tree_data), 
              aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
    theme_dendro()
  
  # plot region space
  preg <- ggplot(Auto, aes(weight, horsepower)) +
    geom_point(alpha = .2) +
    geom_segment(data = partition.tree.data(mod)[[1]],
                 aes(x = xmin, xend = xmax, y = ymin, yend = ymax)) +
    geom_text(data = partition.tree.data(mod)[[2]],
              aes(x = x, y = y, label = label)) +
    coord_cartesian(xlim = c(min(Auto$weight), max(Auto$weight)),
                    ylim = c(min(Auto$horsepower), max(Auto$horsepower)),
                    expand = FALSE) +
    theme(panel.border = element_rect(fill = NA, size = 1))
  
  # display plots side by side
  grid.arrange(ptree, preg, ncol = 2,
               top = textGrob(str_c("Terminal Nodes = ", i),
                              gp = gpar(fontsize = 20))) 
}

Estimation procedure

We have already identified that decision trees use stratification to divide the observations into \(R_J\) regions. Like in linear regression, our goal is to minimize the residual sum of the squared errors (RSS), defined for a decision tree as:

\[\sum_{j=1}^J \sum_{i \in R_j} (y_i - \hat{y}_{R_j})^2\]

where \(\hat{y}_{R_j}\) is the mean response for the observations in the \(j\)th region. In order to do this, decision trees implement a recursive binary strategy. The process begins at the top of the tree (top-down) and successively splits the data into a new region. This split generates two new branches in the tree. Rather than looking forward to select the optimal split among all future possibilities, this approach is greedy in that it selects the best split at that particular step. Given all the potential splits that could be performed on one of the predictors \(X_1, X_2, \dots, X_p\) predictors, the algorithm assigns a cutpoint \(s\) that splits the data in the manner that reduces the RSS by the largest amount. As the number of predictors \(p\) and observations \(N\) increases, the more potential cutpoints the algorithm must consider. However even with relatively large numbers of predictors and observations, the computational process is quite efficient.

This process continues until some designated stopping criteria is reached, otherwise it could continue until each training observation is sorted into its own node (i.e. overfitting). For example, by default the tree() function in R (from the tree library) will not split a node if a resulting node would contain fewer than 10 training observations. Once this iterative process stops, we can generate predicted values for the response of a given test observation by calculating the mean of the training observations for the region in which the test observation belongs.

Pruning the tree

Notice that we stop splitting the tree in order to prevent overfitting. Even with the above process, decision trees are highly susceptible to overfitting due to its natural complexity. And if we simply set the stopping criteria at a higher level, we may miss crucial branches later on in the process. Instead we want a method that allows us to grow a large tree and preserve the most important branches or elements.

In essence, we want to prune the tree. Cost complexity pruning is one predominant method for achieving this goal. While I leave the mathematics of this operation to ISL, cost complexity pruning uses a tuning parameter to selectively prune or snip branches that do not contribute significant predictive accuracy, resulting in a subtree generated from the full tree. Different tuning parameter values will lead to different trade-offs between model complexity and model accuracy, so we can use function such as prune.tree() in conjunction with \(k\)-fold cross-validation to select a cost complexity parameter that optimally balances the trade-off for the specific dataset.2

For example, here is the full tree grown for the horsepower + weight decision tree:

auto_tree <- tree(mpg ~ horsepower + weight, data = Auto,
     control = tree.control(nobs = nrow(Auto),
                            mindev = 0))
mod <- auto_tree

# plot tree
tree_data <- dendro_data(mod)
ptree <- ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro()

# plot region space
preg <- ggplot(Auto, aes(weight, horsepower)) +
  geom_point(alpha = .2) +
  geom_segment(data = partition.tree.data(mod)[[1]],
               aes(x = xmin, xend = xmax, y = ymin, yend = ymax)) +
  geom_text(data = partition.tree.data(mod)[[2]],
            aes(x = x, y = y, label = label)) +
  coord_cartesian(xlim = c(min(Auto$weight), max(Auto$weight)),
                  ylim = c(min(Auto$horsepower), max(Auto$horsepower)),
                  expand = FALSE) +
  theme(panel.border = element_rect(fill = NA, size = 1))

# display plots side by side
grid.arrange(ptree, preg, ncol = 2,
             top = textGrob(str_c("Terminal Nodes = ", ceiling(length(mod$frame$yval) / 2)),
                            gp = gpar(fontsize = 20)))

Let’s use \(10\)-fold CV to select the optimal tree size:

# generate 10-fold CV trees
auto_cv <- crossv_kfold(Auto, k = 10) %>%
  mutate(tree = map(train, ~ tree(mpg ~ horsepower + weight, data = .,
     control = tree.control(nobs = nrow(Auto),
                            mindev = 0))))

# calculate each possible prune result for each fold
auto_cv <- expand.grid(auto_cv$.id, 2:10) %>%
  as_tibble() %>%
  mutate(Var2 = as.numeric(Var2)) %>%
  rename(.id = Var1,
         k = Var2) %>%
  left_join(auto_cv) %>%
  mutate(prune = map2(tree, k, ~ prune.tree(.x, best = .y)),
         mse = map2_dbl(prune, test, mse))

auto_cv %>%
  select(k, mse) %>%
  group_by(k) %>%
  summarize(test_mse = mean(mse),
            sd = sd(mse, na.rm = TRUE)) %>%
  ggplot(aes(k, test_mse)) +
  geom_point() +
  geom_line() +
  labs(x = "Number of terminal nodes",
       y = "Test MSE")

The minimum cross-validated test MSE is for 7 terminal nodes. Here’s what that tree looks like:

mod <- prune.tree(auto_tree, best = 7)

# plot tree
tree_data <- dendro_data(mod)
ptree <- ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro()

# plot region space
preg <- ggplot(Auto, aes(weight, horsepower)) +
  geom_point(alpha = .2) +
  geom_segment(data = partition.tree.data(mod)[[1]],
               aes(x = xmin, xend = xmax, y = ymin, yend = ymax)) +
  geom_text(data = partition.tree.data(mod)[[2]],
            aes(x = x, y = y, label = label)) +
  coord_cartesian(xlim = c(min(Auto$weight), max(Auto$weight)),
                  ylim = c(min(Auto$horsepower), max(Auto$horsepower)),
                  expand = FALSE) +
  theme(panel.border = element_rect(fill = NA, size = 1))

# display plots side by side
grid.arrange(ptree, preg, ncol = 2,
             top = textGrob(str_c("Terminal Nodes = ", ceiling(length(mod$frame$yval) / 2)),
                            gp = gpar(fontsize = 20)))

Classification trees

A classification tree is similar to a regression tree, except that the response variable is qualitative. In making predictions, we would predict for a test set observation the most commonly occurring class value in the given region. However we will also consider the class proportions, or the proportion of training observations in the region \(R_j\) that fall into a given class.

Rather than using RSS to grow the tree, we have three options for minimizing error. An obvious choice might be the classification error rate, or the proportion of training observations in a given region that do not belong to the most common class:

\[E = 1 - \max_{k}(\hat{p}_{mk})\]

where \(\hat{p}_{mk}\) is the proportion of training observations in region \(m\) that do not belong to the most common class \(k\).

In practice, two other methods grow better and more accurate trees. The Gini index is defined as:

\[G = \sum_{k = 1}^k \hat{p}_{mk} (1 - \hat{p}_{mk})\]

and is a measure of node purity. The higher the proportion of observations belonging to a single class, the closer this value will be to 0.

The alternative is cross-entropy:

\[D = - \sum_{k = 1}^K \hat{p}_{mk} \log(\hat{p}_{mk})\]

As more observations are closer to or near 0 or 1, cross-entropy will shrink towards zero. So for classification trees, each split can be evaluated using one of these criteria, though again it is typically the Gini index or cross-entropy.3

Let’s return to our running Titanic example. I want to predict who lives and who dies during this event. Instead of using logistic regression, I’m going to calculate a decision tree based on a passenger’s age and gender. Here’s what that decision tree looks like:

titanic <- titanic_train %>%
  as_tibble() %>%
  mutate(Survived = factor(Survived, levels = 0:1, labels = c("Died", "Survived")),
         Female = factor(Sex, levels = c("male", "female")))

# estimate model
titanic_tree <- tree(Survived ~ Age + Female, data = titanic,
                     control = tree.control(nobs = nrow(titanic),
                            mindev = .001))

# plot unpruned tree
mod <- titanic_tree

tree_data <- dendro_data(mod)
ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro() +
  labs(title = "Titanic survival tree",
       subtitle = "Age + Gender")

err.rate.tree <- function(model, data) {
  data <- as_tibble(data)
  response <- as.character(model$terms[[2]])
  
  pred <- predict(model, newdata = data, type = "class")
  actual <- data[[response]]
  
  return(mean(pred != actual, na.rm = TRUE))
}

# generate 10-fold CV trees
titanic_cv <- titanic %>%
  na.omit() %>%
  crossv_kfold(k = 10) %>%
  mutate(tree = map(train, ~ tree(Survived ~ Age + Female, data = .,
     control = tree.control(nobs = nrow(titanic),
                            mindev = .001))))

# calculate each possible prune result for each fold
titanic_cv <- expand.grid(titanic_cv$.id,
                          seq(from = 2, to = ceiling(length(mod$frame$yval) / 2))) %>%
  as_tibble() %>%
  mutate(Var2 = as.numeric(Var2)) %>%
  rename(.id = Var1,
         k = Var2) %>%
  left_join(titanic_cv) %>%
  mutate(prune = map2(tree, k, ~ prune.misclass(.x, best = .y)),
         mse = map2_dbl(prune, test, err.rate.tree))

titanic_cv %>%
  group_by(k) %>%
  summarize(test_mse = mean(mse),
            sd = sd(mse, na.rm = TRUE)) %>%
  ggplot(aes(k, test_mse)) +
  geom_point() +
  geom_line() +
  labs(title = "Titanic survival tree",
       subtitle = "Age + Gender",
       x = "Number of terminal nodes",
       y = "Test error rate")

Here I select 6 as the optimal number of nodes.

mod <- prune.tree(titanic_tree, best = 6)

tree_data <- dendro_data(mod)
ggplot(segment(tree_data)) +
  geom_segment(aes(x = x, y = y, xend = xend, yend = yend), 
               alpha = 0.5) +
  geom_text(data = label(tree_data), 
            aes(x = x, y = y, label = label_full), vjust = -0.5, size = 3) +
  geom_text(data = leaf_label(tree_data), 
            aes(x = x, y = y, label = label), vjust = 0.5, size = 3) +
  theme_dendro() +
  labs(title = "Titanic survival tree",
       subtitle = "Age + Gender")

Notice that some branches split and lead to the same outcome. For instance, the bottom-left branch assigns males with an age less than 13 but both greater than and less than \(24.75\) to Died. This is because splitting the node leads to increased node purity where we are even more confident in our predictions. Think about it. Here are the outcomes in the training observations for males older than 13 years old:

titanic_m13 <- titanic %>%
  filter(Female == "male", Age >= 13) %>%
  count(Survived)

knitr::kable(titanic_m13,
             caption = "Males older than or equal to 13 on the Titanic",
             col.names = c("Outcome", "Number of training observations"))
Males older than or equal to 13 on the Titanic
Outcome Number of training observations
Died 344
Survived 72

We would predict for all of these observations that the individual died, being incorrect 72 times. What happens if we split this subset even further?

titanic %>%
  filter(Female == "male", Age >= 13) %>%
  mutate(age25 = Age < 24.75) %>%
  count(age25, Survived) %>%
  complete(age25, Survived, fill = list(n = 0)) %>%
  knitr::kable(col.names = c("Less than 24.75 years old", "Outcome", "Number of training observations"))
Less than 24.75 years old Outcome Number of training observations
FALSE Died 232
FALSE Survived 60
TRUE Died 112
TRUE Survived 12

For males between 13 and 24.75 years old, the decision tree prediction achieves higher node purity - it more accurately predicts all of the training observations as dead. If we had a test observation for a 16-year-old male, we’d be more confident in our prediction than if we had terminated the node at \(\text{age} < 13\). While this does not improve our error rate (we would have made the same prediction regardless), it does improve our Gini index and cross-entropy which are the measures used to grow the tree.

Trees vs. regression

Linear regression and decision trees utilize entirely different functional forms. Linear regression assumes linear and additive relationships between predictors and the response:

\[f(X) = \beta_0 + \sum_{j = 1}^p X_j \beta_j\]

Whereas decision trees assume the observations can be partitioned into the feature space:

\[f(X) = \sum_{m = 1}^M c_m \cdot 1_{X \in R_m}\]

If the relationship between the predictor(s) and the response are truly linear and additive, then linear regression will likely perform better than a decision tree. If the relationship is highly complex and non-linear, then decision trees may be the better option. Using resampling methods such as cross-validation can help you to decide the appropriate statistical learning method.

Benefits/drawbacks to decision trees

Decision trees are an entirely different method of estimating functional forms as compared to linear regression. There are some benefits to trees:

  • They are easy to explain. Most people, even if they lack statistical training, can understand decision trees.
  • They are easily presented as visualizations, and pretty interpretable.
  • Qualitative predictors are easily handled without the need to create a long series of dummy variables.

However there are also drawbacks to trees:

  • Their accuracy rates are generally lower than other regression and classification approaches.
  • Trees can be non-robust. That is, a small change in the data or inclusion/exclusion of a handful of observations can dramatically alter the final estimated tree. For example, let’s estimate a decision tree for the highway mileage example (\(N = 392\)) by splitting the data into a training/test set (70/30%) and estimating the test MSE, and repeat this process 1000 times using random combinations of training/test sets:

    auto_val_test <- function(){
      # split data
      auto_split <- resample_partition(Auto, p = c(test = 0.3, train = 0.7))
    
      # estimate model
      val <- tree(mpg ~ horsepower + weight, data = auto_split$train)
    
      # estimate test mse
      mse(val, auto_split$test)
    }
    
    # repeat the procedure 100 times
    val_mse <- data_frame(id = 1:1000,
                          mse = map_dbl(id, ~ auto_val_test()))
    
    # distribution of the mse
    ggplot(val_mse, aes(mse)) +
      geom_histogram() +
      geom_vline(xintercept = mean(val_mse$mse))

The distribution of test MSEs is quite large for each of the splits, indicating substantial variance in our estimate of the test MSE. Fortunately, there is an easy way to improve on these poor predictions: by aggregating many decision trees and averaging across them, we can substantially improve performance.

Bagging

Decision trees suffer from high variance: as we saw above, even a small change in the training/test set partitions can lead to substantial changes in the estimated model and resulting fit. However a method implementing low variance should provide more consistent estimates, regardless of the sample split. By bootstrap aggregating, or simply bagging, is a general method for reducing variance in estimates.

We already met the bootstrap. Recall that this involves repeatedly sampling with replacement from a sample, estimating a parameter or set of parameters for each bootstrap sample, then averaging across the bootstrap samples to form our bootstrap estimate of the parameter. By averaging across all the bootstrap samples, we reduce the variance \(\sigma^2\) in our final estimate.4

As this applies to statistical learning methods, we estimate \(\hat{f}^1(x), \hat{f}^2(x), \dots, \hat{f}^B(x)\) using \(B\) separate training sets, and average across the models to generate a single low-variance model:

\[\hat{f}_{\text{avg}}(x) = \frac{1}{B} \sum_{b = 1}^B \hat{f}^b(x)\]

Since we don’t have that many training sets, we bootstrap them, just like how we estimated bootstrap parameters for a linear regression model. We estimate a decision tree model on each bootstrap sample and average the results of the models to generate the bagged estimate:

\[\hat{f}_{\text{bag}}(x) = \frac{1}{B} \sum_{b = 1}^B \hat{f}^b(x)\]

Each tree is grown without pruning, so they are high-variance but low-bias. Then by averaging across the results, we should get an estimate that has low-bias and low-variance. For regression trees this is straight-forward. For classification trees, we estimate \(B\) trees and for a given test observation assign it the majority-class result: the overall prediction is the most commonly occurring predicted outcome across all the \(B\) predictions. Compared to the error rate for the corresponding classification tree, bagged estimates generally have slightly lower error rates.

Out-of-bag estimates

Fortunately using a bagged approach also allows us to avoid using any type of resampling method to calculate the test MSE or error rate. This is because we have a natural test set as a result of the bootstrapping process. Recall that in a bootstrap sampling process, we sample with replacement. This means that in some bootstrap samples, an observation may never be drawn. In fact, there is a pattern to this phenomenon. On average, each bagged tree uses approximately two-thirds of the original observations. Therefore observations not appearing in a given bag are considered out-of-bag observations (OOB).

# generate sample index
samp <- data_frame(x = seq.int(1000))

# generate bootstrap sample and count proportion of observations in each draw
prop_drawn <- bootstrap(samp, n = nrow(samp)) %>%
  mutate(strap = map(strap, as_tibble)) %>%
  unnest(strap) %>%
  mutate(drawn = TRUE) %>%
  complete(.id, x, fill = list(drawn = FALSE)) %>%
  distinct %>%
  group_by(x) %>%
  mutate(n_drawn = cumsum(drawn),
         .id = as.numeric(.id),
         n_prop = n_drawn / .id)

ggplot(prop_drawn, aes(.id, n_prop, group = x)) +
  geom_line(alpha = .05) +
  labs(x = "b-th bootstrap sample ",
       y = "Proportion i-th observation in samples 1:b")

Because of this, we can calculate the out-of-bag error estimate, or the average error estimate for out-of-bag observations. First we generate bagged predictions for each observation \(i\) using only its OOB estimates, then we average across all \(i\) observations to get the OOB error estimate. This is a valid estimate of the test error rate/MSE because it only uses observations that were not part of the training observations for a given bag \(b\). This is far more computationally advantageous than calculating a cross-validated error rate for a bagged model. Consider the following example predicting survival on the Titanic using all available predictors in the dataset:5

titanic_rf_data <- titanic %>%
    select(-Name, -Ticket, -Cabin, -Sex, -PassengerId) %>%
    mutate_each(funs(as.factor(.)), Pclass, Embarked) %>%
    na.omit

(titanic_bag <- randomForest(Survived ~ ., data = titanic_rf_data,
                             mtry = 7, ntree = 500))
## 
## Call:
##  randomForest(formula = Survived ~ ., data = titanic_rf_data,      mtry = 7, ntree = 500) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 7
## 
##         OOB estimate of  error rate: 20.9%
## Confusion matrix:
##          Died Survived class.error
## Died      356       68       0.160
## Survived   81      209       0.279
Estimation time for OOB error rate
system.time({
  randomForest(Survived ~ ., data = titanic_rf_data,
                              mtry = 7, ntree = 500)
})
##    user  system elapsed 
##   0.426   0.014   0.440
Estimation time for \(10\)-fold CV error rate
system.time({
  crossv_kfold(titanic_rf_data, k = 10) %>%
    mutate(model = map(train, ~ randomForest(Survived ~ ., data = .,
                              mtry = 7, ntree = 500)),
           test.err = map2_dbl(model, test, err.rate.rf)) %>%
    summarize(mean(test.err))
})
##    user  system elapsed 
##   2.899   0.085   2.992

For our Titanic bagged model with all available predictors, we estimate an OOB error rate of \(20.868\%\). Likewise, we obtain a confusion matrix to identify our error rate for each class.

Variable importance measures

Interpreting a bagged model is much more difficult than interpreting a single decision tree. Because each tree is unique, we cannot plot an “average” of the trees like we might with a bootstrapped linear model. The most common method of interpretation (beyond prediction accuracy) is variable importance, or attempting to assess how important each variable is to the model. In regression trees, for each predictor we calculate the total amount of reduction in the RSS attributable to splits caused by the predictor, averaged over the \(B\) trees. For classification trees, we do the same thing using average reduction in the Gini index.

data_frame(var = rownames(importance(titanic_bag)),
           MeanDecreaseGini = importance(titanic_bag)[,1]) %>%
  mutate(var = fct_reorder(var, MeanDecreaseGini, fun = median)) %>%
  ggplot(aes(var, MeanDecreaseGini)) +
  geom_point() +
  coord_flip() +
  labs(title = "Predicting survival on the Titanic",
       subtitle = "Bagging",
       x = NULL,
       y = "Average decrease in the Gini Index")

For classification trees, larger values are better. So for the Titanic bagged model, gender, age, and fare are the most important predictors, whereas number of siblings/parents aboard and the port of departure are relatively unimportant.

Random forests

Random forests improve upon bagging by decorrelating the individual trees. The problem with bagging is that if there is a single dominant predictor in the dataset, most trees will use the same predictor for the first split and ensure correlation and similarity among the trees. Remember that the goal of bagging is to reduce the variance of our estimates of the response variable \(Y\). But averaging across a set of correlated trees will not substantially reduce variance, at least not as much as if the trees were uncorrelated.

To resolve this problem, when splitting a tree random forests will only consider a random sample \(m\) of the total possible predictors \(p\). That is, it intentionally ignores a random set of variables. Every time a new split is considered, a new random sample \(m\) is drawn. The main question then becomes how to select the size of \(m\). ISL recommends \(m = \sqrt{p}\). By default, the randomForest package uses \(m = \sqrt{p}\) for classification trees and \(m = \frac{p}{3}\) for regression trees.

Let’s compare the results of the bagged Titanic model to the same model, only this time employing the random forest method:

Bagged model
titanic_bag
## 
## Call:
##  randomForest(formula = Survived ~ ., data = titanic_rf_data,      mtry = 7, ntree = 500) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 7
## 
##         OOB estimate of  error rate: 20.2%
## Confusion matrix:
##          Died Survived class.error
## Died      358       66       0.156
## Survived   78      212       0.269
seq.int(titanic_bag$ntree) %>%
  map_df(~ getTree(titanic_bag, k = ., labelVar = TRUE)[1,]) %>%
  count(`split var`) %>%
  knitr::kable(caption = "Variable used to generate the first split in each tree",
               col.names = c("Variable used to split", "Number of training observations"))
Variable used to generate the first split in each tree
Variable used to split Number of training observations
Female 500
Random forest model
(titanic_rf <- randomForest(Survived ~ ., data = titanic_rf_data,
                            ntree = 500))
## 
## Call:
##  randomForest(formula = Survived ~ ., data = titanic_rf_data,      ntree = 500) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 2
## 
##         OOB estimate of  error rate: 18.4%
## Confusion matrix:
##          Died Survived class.error
## Died      382       42      0.0991
## Survived   89      201      0.3069
seq.int(titanic_rf$ntree) %>%
  map_df(~ getTree(titanic_rf, k = ., labelVar = TRUE)[1,]) %>%
  count(`split var`) %>%
  knitr::kable(caption = "Variable used to generate the first split in each tree",
               col.names = c("Variable used to split", "Number of training observations"))
Variable used to generate the first split in each tree
Variable used to split Number of training observations
Age 44
Embarked 66
Fare 99
Female 140
Parch 30
Pclass 112
SibSp 9
data_frame(var = rownames(importance(titanic_rf)),
           `Random forest` = importance(titanic_rf)[,1]) %>%
  left_join(data_frame(var = rownames(importance(titanic_rf)),
           Bagging = importance(titanic_bag)[,1])) %>%
  mutate(var = fct_reorder(var, Bagging, fun = median)) %>%
  gather(model, gini, -var) %>%
  ggplot(aes(var, gini, color = model)) +
  geom_point() +
  coord_flip() +
  labs(title = "Predicting survival on the Titanic",
       x = NULL,
       y = "Average decrease in the Gini Index",
       color = "Method")

The OOB error rate is a couple points smaller on the random forest model, and in the random forest model gender is no longer exclusively used to generate the first split for each tree. We can also observe that the average decrease in the Gini index associated with each variable is generally smaller using the random forest method compared to bagging - this is because of the variable restriction imposed when considering splits.

Boosting

Boosting is another approach to improve upon the result of a single decision tree. Instead of creating multiple independent decision trees through a bootstrapping process, boosting grows trees sequentially, using information from the previously grown trees. Rather than fitting a model to the response variable \(Y\), we fit a large number of decision trees \(\hat{f}^1, \dots, \hat{f}^B\) to the current residuals. Each time a new decision tree is estimated, the residuals are updated combining the results of all previous decision trees in preparation for fitting the next tree.

Rather than learning hard and fast like in bagging and random forests, boosting learns slowly over time as new trees are added. Because boosting is additive and slow, we can estimate fairly small trees and still gain considerable predictive power.

Boosting is a general process that can be used for other statistical learning methods. The three main tuning parameters when boosting are:

  1. The number of trees \(B\). If \(B\) is too large, boosting can overfit. Typically we would use cross-validation to select \(B\).
  2. The shrinkage parameter \(\lambda\), which is a small positive number (i.e. \(.01\) or \(.001\)). This controls the rate at which boosting learns. As \(\lambda\) gets smaller, \(B\) generally must increase.
  3. The number of \(d\) split in each tree. Surprisingly, \(d=1\) actually works well which is essentially an additive model (each tree is a stump with a single predictor), though larger values of \(d\) are also common.

Let’s evaluate all the approaches we’ve seen so far using the Titanic model.

titanic_split <- resample_partition(titanic_rf_data, p = c("test" = .3,
                                                           "train" = .7))

titanic_models <- list("bagging" = randomForest(Survived ~ ., data = titanic_split$train,
                                                mtry = 7, ntree = 10000),
                       "rf_mtry2" = randomForest(Survived ~ ., data = titanic_split$train,
                                                 mtry = 2, ntree = 10000),
                       "rf_mtry4" = randomForest(Survived ~ ., data = titanic_split$train,
                                                 mtry = 4, ntree = 10000),
                       "boosting_depth1" = gbm(as.numeric(Survived) - 1 ~ .,
                                               data = titanic_split$train,
                                               n.trees = 10000, interaction.depth = 1),
                       "boosting_depth2" = gbm(as.numeric(Survived) - 1 ~ .,
                                               data = titanic_split$train,
                                               n.trees = 10000, interaction.depth = 2),
                       "boosting_depth4" = gbm(as.numeric(Survived) - 1 ~ .,
                                               data = titanic_split$train,
                                               n.trees = 10000, interaction.depth = 4))
## Distribution not specified, assuming bernoulli ...
## Distribution not specified, assuming bernoulli ...
## Distribution not specified, assuming bernoulli ...
boost_test_err <- data_frame(bagging = predict(titanic_models$bagging,
                                               newdata = as_tibble(titanic_split$test),
                                               predict.all = TRUE)[[2]] %>%
                               apply(2, function(x) x != as_tibble(titanic_split$test)$Survived) %>%
                               apply(2, mean),
                             rf_mtry2 = predict(titanic_models$rf_mtry2,
                                                newdata = as_tibble(titanic_split$test),
                                                predict.all = TRUE)[[2]] %>%
                               apply(2, function(x) x != as_tibble(titanic_split$test)$Survived) %>%
                               apply(2, mean),
                             rf_mtry4 = predict(titanic_models$rf_mtry4,
                                                newdata = as_tibble(titanic_split$test),
                                                predict.all = TRUE)[[2]] %>%
                               apply(2, function(x) x != as_tibble(titanic_split$test)$Survived) %>%
                               apply(2, mean),
                             boosting_depth1 = predict(titanic_models$boosting_depth1,
                                                       newdata = as_tibble(titanic_split$test),
                                                       n.trees = 1:10000) %>%
                               apply(2, function(x) round(x) == as.numeric(as_tibble(titanic_split$test)$Survived) - 1) %>%
                               apply(2, mean),
                             boosting_depth2 = predict(titanic_models$boosting_depth2,
                                                       newdata = as_tibble(titanic_split$test),
                                                       n.trees = 1:10000) %>%
                               apply(2, function(x) round(x) == as.numeric(as_tibble(titanic_split$test)$Survived) - 1) %>%
                               apply(2, mean),
                             boosting_depth4 = predict(titanic_models$boosting_depth4,
                                                       newdata = as_tibble(titanic_split$test),
                                                       n.trees = 1:10000) %>%
                               apply(2, function(x) round(x) == as.numeric(as_tibble(titanic_split$test)$Survived) - 1) %>%
                               apply(2, mean))

boost_test_err %>%
  mutate(id = row_number()) %>%
  mutate_each(funs(cummean(.)), bagging:rf_mtry4) %>%
  gather(model, err, -id) %>%
  mutate(model = factor(model, levels = names(titanic_models),
                        labels = c("Bagging", "Random forest: m = \\sqrt(p)",
                                   "Random forest: m = 4",
                                   "Boosting: depth = 1",
                                   "Boosting: depth = 2",
                                   "Boosting: depth = 4"))) %>%
  ggplot(aes(id, err, color = model)) +
  geom_line() +
  scale_color_brewer(type = "qual", palette = "Dark2") +
  labs(x = "Number of trees",
       y = "Test classification error",
       color = "Model")

Using bagging or random forest methods, the models quickly converge on a test classification error rate. This helps to demonstrate that for bagging and random forests, you do not need a particularly large \(B\) to build a good model. For boosting, additional trees are necessary for the error rate to begin converging and stabilizing around a single value. We can use the gbm.perf() function to help determine the optimal number of boosting iterations based on either OOB, test set, or CV estimates of the error rate/MSE:

data_frame(depth = c(1, 2, 4),
           model = titanic_models[c("boosting_depth1", "boosting_depth2", "boosting_depth4")],
           optimal = map_dbl(model, gbm.perf, plot.it = FALSE)) %>%
  select(-model) %>%
  knitr::kable(caption = "Optimal number of boosting iterations",
               col.names = c("Depth", "Optimal number of iterations"))
## Using OOB method...
## Using OOB method...
## Using OOB method...
Optimal number of boosting iterations
Depth Optimal number of iterations
1 3909
2 3371
4 2436

Session Info

devtools::session_info()
##  setting  value                       
##  version  R version 3.5.1 (2018-07-02)
##  system   x86_64, darwin15.6.0        
##  ui       X11                         
##  language (EN)                        
##  collate  en_US.UTF-8                 
##  tz       America/Chicago             
##  date     2019-01-02                  
## 
##  package      * version date       source        
##  assertthat     0.2.0   2017-04-11 CRAN (R 3.5.0)
##  backports      1.1.2   2017-12-13 CRAN (R 3.5.0)
##  base         * 3.5.1   2018-07-05 local         
##  base64enc      0.1-3   2015-07-28 CRAN (R 3.5.0)
##  bindr          0.1.1   2018-03-13 CRAN (R 3.5.0)
##  bindrcpp       0.2.2   2018-03-29 CRAN (R 3.5.0)
##  broom        * 0.5.0   2018-07-17 CRAN (R 3.5.0)
##  cellranger     1.1.0   2016-07-27 CRAN (R 3.5.0)
##  cli            1.0.0   2017-11-05 CRAN (R 3.5.0)
##  colorspace     1.3-2   2016-12-14 CRAN (R 3.5.0)
##  compiler       3.5.1   2018-07-05 local         
##  crayon         1.3.4   2017-09-16 CRAN (R 3.5.0)
##  datasets     * 3.5.1   2018-07-05 local         
##  devtools       1.13.6  2018-06-27 CRAN (R 3.5.0)
##  digest         0.6.18  2018-10-10 cran (@0.6.18)
##  dplyr        * 0.7.8   2018-11-10 cran (@0.7.8) 
##  evaluate       0.11    2018-07-17 CRAN (R 3.5.0)
##  forcats      * 0.3.0   2018-02-19 CRAN (R 3.5.0)
##  gbm          * 2.1.3   2017-03-21 CRAN (R 3.5.0)
##  ggdendro     * 0.1-20  2016-04-27 CRAN (R 3.5.0)
##  ggplot2      * 3.1.0   2018-10-25 cran (@3.1.0) 
##  glue           1.3.0   2018-07-17 CRAN (R 3.5.0)
##  graphics     * 3.5.1   2018-07-05 local         
##  grDevices    * 3.5.1   2018-07-05 local         
##  grid         * 3.5.1   2018-07-05 local         
##  gridExtra    * 2.3     2017-09-09 CRAN (R 3.5.0)
##  gtable         0.2.0   2016-02-26 CRAN (R 3.5.0)
##  haven          1.1.2   2018-06-27 CRAN (R 3.5.0)
##  hms            0.4.2   2018-03-10 CRAN (R 3.5.0)
##  htmltools      0.3.6   2017-04-28 CRAN (R 3.5.0)
##  httr           1.3.1   2017-08-20 CRAN (R 3.5.0)
##  ISLR         * 1.2     2017-10-20 CRAN (R 3.5.0)
##  jsonlite       1.5     2017-06-01 CRAN (R 3.5.0)
##  knitr          1.20    2018-02-20 CRAN (R 3.5.0)
##  lattice      * 0.20-35 2017-03-25 CRAN (R 3.5.1)
##  lazyeval       0.2.1   2017-10-29 CRAN (R 3.5.0)
##  lubridate      1.7.4   2018-04-11 CRAN (R 3.5.0)
##  magrittr       1.5     2014-11-22 CRAN (R 3.5.0)
##  MASS           7.3-50  2018-04-30 CRAN (R 3.5.1)
##  Matrix         1.2-14  2018-04-13 CRAN (R 3.5.1)
##  memoise        1.1.0   2017-04-21 CRAN (R 3.5.0)
##  methods      * 3.5.1   2018-07-05 local         
##  modelr       * 0.1.2   2018-05-11 CRAN (R 3.5.0)
##  munsell        0.5.0   2018-06-12 CRAN (R 3.5.0)
##  nlme           3.1-137 2018-04-07 CRAN (R 3.5.1)
##  parallel     * 3.5.1   2018-07-05 local         
##  pillar         1.3.0   2018-07-14 CRAN (R 3.5.0)
##  pkgconfig      2.0.2   2018-08-16 CRAN (R 3.5.1)
##  plyr           1.8.4   2016-06-08 CRAN (R 3.5.0)
##  pROC         * 1.12.1  2018-05-06 CRAN (R 3.5.0)
##  purrr        * 0.2.5   2018-05-29 CRAN (R 3.5.0)
##  R6             2.3.0   2018-10-04 cran (@2.3.0) 
##  randomForest * 4.6-14  2018-03-25 CRAN (R 3.5.0)
##  rcfss        * 0.1.5   2018-05-30 local         
##  Rcpp           1.0.0   2018-11-07 cran (@1.0.0) 
##  readr        * 1.1.1   2017-05-16 CRAN (R 3.5.0)
##  readxl         1.1.0   2018-04-20 CRAN (R 3.5.0)
##  rlang          0.3.0.1 2018-10-25 CRAN (R 3.5.0)
##  rmarkdown      1.10    2018-06-11 CRAN (R 3.5.0)
##  rprojroot      1.3-2   2018-01-03 CRAN (R 3.5.0)
##  rstudioapi     0.7     2017-09-07 CRAN (R 3.5.0)
##  rvest          0.3.2   2016-06-17 CRAN (R 3.5.0)
##  scales         1.0.0   2018-08-09 CRAN (R 3.5.0)
##  splines      * 3.5.1   2018-07-05 local         
##  stats        * 3.5.1   2018-07-05 local         
##  stringi        1.2.4   2018-07-20 CRAN (R 3.5.0)
##  stringr      * 1.3.1   2018-05-10 CRAN (R 3.5.0)
##  survival     * 2.42-6  2018-07-13 CRAN (R 3.5.0)
##  tibble       * 1.4.2   2018-01-22 CRAN (R 3.5.0)
##  tidyr        * 0.8.1   2018-05-18 CRAN (R 3.5.0)
##  tidyselect     0.2.5   2018-10-11 cran (@0.2.5) 
##  tidyverse    * 1.2.1   2017-11-14 CRAN (R 3.5.0)
##  titanic      * 0.1.0   2015-08-31 CRAN (R 3.5.0)
##  tools          3.5.1   2018-07-05 local         
##  tree         * 1.0-39  2018-03-17 CRAN (R 3.5.0)
##  utils        * 3.5.1   2018-07-05 local         
##  withr          2.1.2   2018-03-15 CRAN (R 3.5.0)
##  xml2           1.2.0   2018-01-24 CRAN (R 3.5.0)
##  yaml           2.2.0   2018-07-25 CRAN (R 3.5.0)

  1. Yes, we know the pitfalls of using training MSE for model comparison. It’s just an example because we haven’t split the data into a validation set.

  2. Alternatively, you specify the total number of terminal nodes to keep in the model and select that value to optimally balance the trade-off between complexity and accuracy.

  3. tree() uses the Gini index for classification trees.

  4. The variance for each observation in an independent sample \(Z_1, Z_2, \dots, Z_n\) is \(\sigma^2\). The variance for the average of the sample \(\bar{Z}\) is \(\frac{\sigma^2}{n}\). By averaging across the observations, we reduce the estimated variance. Intuitively this makes sense because our estimate of \(\bar{Z}\) is based on more information, and should therefore be more stable.

  5. This includes all variables in the data frame that are not merely text values.

LS0tCnRpdGxlOiAiU3RhdGlzdGljYWwgbGVhcm5pbmc6IHRyZWUtYmFzZWQgbWV0aG9kcyIKYXV0aG9yOiAiTUFDUyAzMDEwMCAtIFBlcnNwZWN0aXZlcyBvbiBDb21wdXRhdGlvbmFsIE1vZGVsaW5nIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFKQpgYGAKCiMgT2JqZWN0aXZlcwoKKiBEZWZpbmUgYSBkZWNpc2lvbiB0cmVlCiogSWRlbnRpZnkgdGhlIHN0ZXBzIHRvIGVzdGltYXRpbmcgYSBkZWNpc2lvbiB0cmVlCiogRGVtb25zdHJhdGUgaG93IHRvIGVzdGltYXRlIGEgZGVjaXNpb24gdHJlZSBmb3IgcmVncmVzc2lvbiBhbmQgY2xhc3NpZmljYXRpb24gcHJvYmxlbXMKKiBEZWZpbmUgYW5kIGVzdGltYXRlIGJhZ2dpbmcgbW9kZWxzCiogRGVmaW5lIGFuZCBlc3RpbWF0ZSByYW5kb20gZm9yZXN0IG1vZGVscwoqIERlZmluZSBhbmQgZXN0aW1hdGUgYm9vc3RpbmcgbW9kZWxzCgpgYGB7ciBwYWNrYWdlcywgY2FjaGUgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkobW9kZWxyKQpsaWJyYXJ5KHRyZWUpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoSVNMUikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeSh0aXRhbmljKQpsaWJyYXJ5KHJjZnNzKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoZ2JtKQojIHRvIGdldCB0aGUgdHJlZSBncmFwaHMgd2l0aCB0aGUgbGFiZWxzIGFuZCB2YWx1ZXMsIHVzZSB0aGUgZm9ya2VkCiMgdmVyc2lvbiBvZiBnZ2RlbmRybwojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYmVuc29sdG9mZi9nZ2RlbmRybyIpCmxpYnJhcnkoZ2dkZW5kcm8pCgpvcHRpb25zKGRpZ2l0cyA9IDMpCnNldC5zZWVkKDEyMzQpCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpCmBgYAoKYGBge3IgZXJyLXJhdGUtcmZ9CmVyci5yYXRlLnJmIDwtIGZ1bmN0aW9uKG1vZGVsLCBkYXRhKSB7CiAgZGF0YSA8LSBhc190aWJibGUoZGF0YSkKICByZXNwb25zZSA8LSBhcy5jaGFyYWN0ZXIobW9kZWwkdGVybXNbWzJdXSkKICAKICBwcmVkIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBkYXRhLCB0eXBlID0gInJlc3BvbnNlIikKICBhY3R1YWwgPC0gZGF0YVtbcmVzcG9uc2VdXQogIAogIHJldHVybihtZWFuKHByZWQgIT0gYWN0dWFsLCBuYS5ybSA9IFRSVUUpKQp9CmBgYAoKIyBEZWNpc2lvbiB0cmVlcwoKIVtdKGh0dHBzOi8vcy1tZWRpYS1jYWNoZS1hazAucGluaW1nLmNvbS9vcmlnaW5hbHMvN2EvODkvZmYvN2E4OWZmNjdiNGNlMzQyMDRjMjMxMzVjYmYzNWFjZmEuanBnKQoKIVtdKGh0dHBzOi8vZWlnaHQybGF0ZS5maWxlcy53b3JkcHJlc3MuY29tLzIwMTYvMDIvNzIxNDUyNTg1NF83MzMyMzdkZDgzX3oxLmpwZz93PTcwMCkKCiFbXShodHRwczovL3MtbWVkaWEtY2FjaGUtYWswLnBpbmltZy5jb20vNTY0eC8wYi84Ny9kZi8wYjg3ZGYxYTU0NDc0NzE2Mzg0ZjhlYzk0YjUyZWFiOS5qcGcpCgoqKkRlY2lzaW9uIHRyZWVzKiogYXJlIGludHVpdGl2ZSBjb25jZXB0cyBmb3IgbWFraW5nIGRlY2lzaW9ucy4gVGhleSBhcmUgYWxzbyB1c2VmdWwgbWV0aG9kcyBmb3IgcmVncmVzc2lvbiBhbmQgY2xhc3NpZmljYXRpb24uIFRoZXkgd29yayBieSBzcGxpdHRpbmcgdGhlIG9ic2VydmF0aW9ucyBpbnRvIGEgbnVtYmVyIG9mIHJlZ2lvbnMsIGFuZCBwcmVkaWN0aW9ucyBhcmUgbWFkZSBiYXNlZCBvbiB0aGUgbWVhbiBvciBtb2RlIG9mIHRoZSB0cmFpbmluZyBvYnNlcnZhdGlvbnMgaW4gdGhhdCByZWdpb24uCgojIyBSZWdyZXNzaW9uIHRyZWVzCgojIyMgU2luZ2xlIHByZWRpY3RvcgoKTGV0J3MgZmlyc3QgY29uc2lkZXIgYSBiYXNpYyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaG9yc2Vwb3dlciBhbmQgaGlnaHdheSBtaWxlYWdlIGZyb20gdGhlIGBBdXRvYCBkYXRhc2V0LgoKYGBge3IgYXV0by1sbX0KIyBhZGQgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHRvIGZpdHRlZCB2YWx1ZXMgZnJvbSBhdWdtZW50KCkKYWRkX2NpIDwtIGZ1bmN0aW9uKGRmX2F1Z21lbnQpIHsKICBkZl9hdWdtZW50ICU+JQogICAgbXV0YXRlKC5maXR0ZWQubG93ID0gLmZpdHRlZCAtIDEuOTYgKiAuc2UuZml0LAogICAgICAgICAgIC5maXR0ZWQuaGlnaCA9IC5maXR0ZWQgKyAxLjk2ICogLnNlLmZpdCkKfQoKIyBkcmF3IDk1JSBjb25maWRlbmNlIGludGVydmFsIHBsb3QgdXNpbmcgcmVzdWx0cyBvZiBhZGRfY2koKQpwbG90X2NpIDwtIGZ1bmN0aW9uKGRmX2NpLCB4KXsKICBnZ3Bsb3QoZGZfY2ksIGFlc19zdHJpbmcoeCwgIi5maXR0ZWQiKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkLmxvdyksIGxpbmV0eXBlID0gMikgKwogIGdlb21fbGluZShhZXMoeSA9IC5maXR0ZWQuaGlnaCksIGxpbmV0eXBlID0gMikKfQoKYXV0b19sbSA8LSBnbG0obXBnIH4gaG9yc2Vwb3dlciwgZGF0YSA9IEF1dG8pCgphdWdtZW50KGF1dG9fbG0sIG5ld2RhdGEgPSBkYXRhX2dyaWQoQXV0bywgaG9yc2Vwb3dlcikpICU+JQogIGFkZF9jaSgpICU+JQogIHBsb3RfY2koImhvcnNlcG93ZXIiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gQXV0bywgYWVzKHkgPSBtcGcpLCBhbHBoYSA9IC4yKSArCiAgbGFicyh0aXRsZSA9ICJMaW5lYXIgbW9kZWwgb2YgaGlnaHdheSBtaWxlYWdlIiwKICAgICAgIHggPSAiSG9yc2Vwb3dlciIsCiAgICAgICB5ID0gIkhpZ2h3YXkgbWlsZWFnZSIpCmBgYAoKQXMgd2UgcmVjYWxsLCBhIHN0cmljdGx5IGxpbmVhciBtb2RlbCBpcyBhIHBvb3IgZml0IGZvciB0aGUgZGF0YSBzaW5jZSB0aGUgcmVsYXRpb25zaGlwIGFjdHVhbGx5IGFwcGVhcnMgdG8gYmUgcXVhZHJhdGljLiBCdXQgdW5sZXNzIHdlIFtyZWxheCBvdXIgbGluZWFyIGFzc3VtcHRpb25dKHBlcnNwMDA3X25vbmxpbmVhci5odG1sKSwgdGhpcyBpcyB0aGUgYmVzdCBPTFMgbW9kZWwgd2UgY2FuIGVzdGltYXRlLgoKTGV0J3MgY29tcGFyZSB0aGlzIGluc3RlYWQgdG8gYSBkZWNpc2lvbiB0cmVlIHVzaW5nIGhvcnNlcG93ZXIgdG8gcHJlZGljdCBoaWdod2F5IG1pbGVhZ2UuIERlY2lzaW9uIHRyZWVzIHdvcmsgdGhyb3VnaCBhIHByb2Nlc3Mgb2YgKipzdHJhdGlmaWNhdGlvbioqOgoKMS4gRGl2aWRlIHRoZSBwcmVkaWN0b3Igc3BhY2UgKCRYXzEsIFhfMiwgXGRvdHMsIFhfcCQpIGludG8gJEokIGRpc3RpbmN0IGFuZCBub24tb3ZlcmxhcHBpbmcgcmVnaW9ucyAkUl8xLCBSXzIsIFxkb3RzLCBSX0okLgoxLiBGb3IgZXZlcnkgb2JzZXJ2YXRpb24gaW4gcmVnaW9uICRSX2okLCB3ZSBtYWtlIHRoZSBzYW1lIHByZWRpY3Rpb24gd2hpY2ggaXMgdGhlIG1lYW4gb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlICRZJCBmb3IgYWxsIG9ic2VydmF0aW9ucyBpbiAkUl9qJC4KClRoaXMgcHJvY2VzcyBpcyBpdGVyYXRpdmU6IGR1cmluZyB0aGUgZmlyc3QgaXRlcmF0aW9uLCB3ZSBzZWdtZW50IHRoZSBwcmVkaWN0b3Igc3BhY2UgJFgkIGludG8gdHdvIHJlZ2lvbnMgJFJfMSwgUl8yJC4gSW4gdGhlIGNvbnRleHQgb2YgYSBkZWNpc2lvbiB0cmVlIHdpdGggYSBzaW5nbGUgcHJlZGljdG9yLCB0aGF0IHByb2Nlc3MgcmVzdWx0cyBpbiBkZWNpc2lvbiB0cmVlcyBsaWtlIHRoZSBmb2xsb3dpbmc6CgpgYGB7ciBwYXJ0LXRyZWUtZGF0YX0KIyBoYWNraXNoIGZ1bmN0aW9uIHRvIGdldCBsaW5lIHNlZ21lbnQgY29vcmRpbmF0ZXMgZm9yIGdncGxvdApwYXJ0aXRpb24udHJlZS5kYXRhIDwtIGZ1bmN0aW9uICh0cmVlLCBsYWJlbCA9ICJ5dmFsIiwgYWRkID0gRkFMU0UsIG9yZHZhcnMsIC4uLikgCnsKICBwdFhsaW5lcyA8LSBmdW5jdGlvbih4LCB2LCB4cmFuZ2UsIHhjb29yZCA9IE5VTEwsIHljb29yZCA9IE5VTEwsIAogICAgICAgICAgICAgICAgICAgICAgIHR2YXIsIGkgPSAxTCkgewogICAgaWYgKHZbaV0gPT0gIjxsZWFmPiIpIHsKICAgICAgeTEgPC0gKHhyYW5nZVsxTF0gKyB4cmFuZ2VbM0xdKS8yCiAgICAgIHkyIDwtICh4cmFuZ2VbMkxdICsgeHJhbmdlWzRMXSkvMgogICAgICByZXR1cm4obGlzdCh4Y29vcmQgPSB4Y29vcmQsIHljb29yZCA9IGMoeWNvb3JkLCB5MSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5MiksIGkgPSBpKSkKICAgIH0KICAgIGlmICh2W2ldID09IHR2YXJbMUxdKSB7CiAgICAgIHhjb29yZCA8LSBjKHhjb29yZCwgeFtpXSwgeHJhbmdlWzJMXSwgeFtpXSwgeHJhbmdlWzRMXSkKICAgICAgeHIgPC0geHJhbmdlCiAgICAgIHhyWzNMXSA8LSB4W2ldCiAgICAgIGxsMiA8LSBSZWNhbGwoeCwgdiwgeHIsIHhjb29yZCwgeWNvb3JkLCB0dmFyLCBpICsgCiAgICAgICAgICAgICAgICAgICAgICAxTCkKICAgICAgeHIgPC0geHJhbmdlCiAgICAgIHhyWzFMXSA8LSB4W2ldCiAgICAgIHJldHVybihSZWNhbGwoeCwgdiwgeHIsIGxsMiR4Y29vcmQsIGxsMiR5Y29vcmQsIHR2YXIsIAogICAgICAgICAgICAgICAgICAgIGxsMiRpICsgMUwpKQogICAgfQogICAgZWxzZSBpZiAodltpXSA9PSB0dmFyWzJMXSkgewogICAgICB4Y29vcmQgPC0gYyh4Y29vcmQsIHhyYW5nZVsxTF0sIHhbaV0sIHhyYW5nZVszTF0sIAogICAgICAgICAgICAgICAgICB4W2ldKQogICAgICB4ciA8LSB4cmFuZ2UKICAgICAgeHJbNExdIDwtIHhbaV0KICAgICAgbGwyIDwtIFJlY2FsbCh4LCB2LCB4ciwgeGNvb3JkLCB5Y29vcmQsIHR2YXIsIGkgKyAKICAgICAgICAgICAgICAgICAgICAgIDFMKQogICAgICB4ciA8LSB4cmFuZ2UKICAgICAgeHJbMkxdIDwtIHhbaV0KICAgICAgcmV0dXJuKFJlY2FsbCh4LCB2LCB4ciwgbGwyJHhjb29yZCwgbGwyJHljb29yZCwgdHZhciwgCiAgICAgICAgICAgICAgICAgICAgbGwyJGkgKyAxTCkpCiAgICB9CiAgICBlbHNlIHN0b3AoIndyb25nIHZhcmlhYmxlIG51bWJlcnMgaW4gdHJlZS4iKQogIH0KICBpZiAoaW5oZXJpdHModHJlZSwgInNpbmdsZW5vZGUiKSkgCiAgICBzdG9wKCJjYW5ub3QgcGxvdCBzaW5nbGVub2RlIHRyZWUiKQogIGlmICghaW5oZXJpdHModHJlZSwgInRyZWUiKSkgCiAgICBzdG9wKCJub3QgbGVnaXRpbWF0ZSB0cmVlIikKICBmcmFtZSA8LSB0cmVlJGZyYW1lCiAgbGVhdmVzIDwtIGZyYW1lJHZhciA9PSAiPGxlYWY+IgogIHZhciA8LSB1bmlxdWUoYXMuY2hhcmFjdGVyKGZyYW1lJHZhclshbGVhdmVzXSkpCiAgaWYgKGxlbmd0aCh2YXIpID4gMkwgfHwgbGVuZ3RoKHZhcikgPCAxTCkgCiAgICBzdG9wKCJ0cmVlIGNhbiBvbmx5IGhhdmUgb25lIG9yIHR3byBwcmVkaWN0b3JzIikKICBubGV2ZWxzIDwtIHNhcHBseShhdHRyKHRyZWUsICJ4bGV2ZWxzIiksIGxlbmd0aCkKICBpZiAoYW55KG5sZXZlbHNbdmFyXSA+IDBMKSkgCiAgICBzdG9wKCJ0cmVlIGNhbiBvbmx5IGhhdmUgY29udGludW91cyBwcmVkaWN0b3JzIikKICB4IDwtIHJlcChOQSwgbGVuZ3RoKGxlYXZlcykpCiAgeFshbGVhdmVzXSA8LSBhcy5kb3VibGUoc3Vic3RyaW5nKGZyYW1lJHNwbGl0c1shbGVhdmVzLCAiY3V0bGVmdCJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMkwsIDEwMEwpKQogIG0gPC0gbW9kZWwuZnJhbWUodHJlZSkKICBpZiAobGVuZ3RoKHZhcikgPT0gMUwpIHsKICAgIHggPC0gc29ydChjKHJhbmdlKG1bW3Zhcl1dKSwgeFshbGVhdmVzXSkpCiAgICBpZiAoaXMubnVsbChhdHRyKHRyZWUsICJ5bGV2ZWxzIikpKSAKICAgICAgeSA8LSBmcmFtZSR5dmFsW2xlYXZlc10KICAgIGVsc2UgeSA8LSBmcmFtZSR5cHJvYlssIDFMXQogICAgeSA8LSBjKHksIHlbbGVuZ3RoKHkpXSkKICAgIGlmIChhZGQpIHsKICAgICAgIyBsaW5lcyh4LCB5LCB0eXBlID0gInMiLCAuLi4pCiAgICB9CiAgICBlbHNlIHsKICAgICAgYSA8LSBhdHRyaWJ1dGVzKGF0dHIobSwgInRlcm1zIikpCiAgICAgIHl2YXIgPC0gYXMuY2hhcmFjdGVyKGEkdmFyaWFibGVzWzEgKyBhJHJlc3BvbnNlXSkKICAgICAgeG8gPC0gbVtbeXZhcl1dCiAgICAgIGlmIChpcy5mYWN0b3IoeG8pKSAKICAgICAgICB5bGltIDwtIGMoMCwgMSkKICAgICAgZWxzZSB5bGltIDwtIHJhbmdlKHhvKQogICAgICAjIHBsb3QoeCwgeSwgeWxhYiA9IHl2YXIsIHhsYWIgPSB2YXIsIHR5cGUgPSAicyIsIHlsaW0gPSB5bGltLAogICAgICAjICAgICAgeGF4cyA9ICJpIiwgLi4uKQogICAgfQogICAgZGF0YV9mcmFtZSh4ID0geCwgeSA9IHkpCiAgfQogIGVsc2UgewogICAgaWYgKCFtaXNzaW5nKG9yZHZhcnMpKSB7CiAgICAgIGluZCA8LSBtYXRjaCh2YXIsIG9yZHZhcnMpCiAgICAgIGlmIChhbnkoaXMubmEoaW5kKSkpIAogICAgICAgIHN0b3AoInVubWF0Y2hlZCBuYW1lcyBpbiB2YXJzIikKICAgICAgdmFyIDwtIG9yZHZhcnNbc29ydChpbmQpXQogICAgfQogICAgbGFiIDwtIGZyYW1lJHl2YWxbbGVhdmVzXQogICAgaWYgKGlzLm51bGwoZnJhbWUkeXByb2IpKSAKICAgICAgbGFiIDwtIGZvcm1hdChzaWduaWYobGFiLCAzTCkpCiAgICBlbHNlIGlmIChtYXRjaChsYWJlbCwgYXR0cih0cmVlLCAieWxldmVscyIpLCBub21hdGNoID0gMEwpKSAKICAgICAgbGFiIDwtIGZvcm1hdChzaWduaWYoZnJhbWUkeXByb2JbbGVhdmVzLCBsYWJlbF0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAzTCkpCiAgICByeCA8LSByYW5nZShtW1t2YXJbMUxdXV0pCiAgICByeCA8LSByeCArIGMoLTAuMDI1LCAwLjAyNSkgKiBkaWZmKHJ4KQogICAgcnogPC0gcmFuZ2UobVtbdmFyWzJMXV1dKQogICAgcnogPC0gcnogKyBjKC0wLjAyNSwgMC4wMjUpICogZGlmZihyeikKICAgIHhyYW5nZSA8LSBjKHJ4LCByeilbYygxLCAzLCAyLCA0KV0KICAgIHhjb29yZCA8LSBOVUxMCiAgICB5Y29vcmQgPC0gTlVMTAogICAgeHkgPC0gcHRYbGluZXMoeCwgZnJhbWUkdmFyLCB4cmFuZ2UsIHhjb29yZCwgeWNvb3JkLCAKICAgICAgICAgICAgICAgICAgIHZhcikKICAgIHh4IDwtIG1hdHJpeCh4eSR4Y29vcmQsIG5yb3cgPSA0TCkKICAgIHl5IDwtIG1hdHJpeCh4eSR5Y29vcmQsIG5yb3cgPSAyTCkKCiAgICByZXR1cm4obGlzdChkYXRhX2ZyYW1lKHhtaW4gPSB4eFsxTCxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICB5bWluID0geHhbMkwsXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeG1heCA9IHh4WzNMLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHltYXggPSB4eFs0TCxdKSwKICAgICAgICAgICAgICAgIGRhdGFfZnJhbWUoeCA9IHl5WzFMLF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB5eVsyTCxdLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IGxhYikpKQogICAgIyBpZiAoIWFkZCkgCiAgICAjICAgcGxvdChyeCwgcnosIHhsYWIgPSB2YXJbMUxdLCB5bGFiID0gdmFyWzJMXSwgdHlwZSA9ICJuIiwgCiAgICAjICAgICAgICB4YXhzID0gImkiLCB5YXhzID0gImkiLCAuLi4pCiAgICAjIHNlZ21lbnRzKHh4WzFMLCBdLCB4eFsyTCwgXSwgeHhbM0wsIF0sIHh4WzRMLCBdKQogICAgIyB0ZXh0KHl5WzFMLCBdLCB5eVsyTCwgXSwgYXMuY2hhcmFjdGVyKGxhYiksIC4uLikKICB9Cn0KYGBgCgpgYGB7ciBhdXRvLXRyZWUyfQojIGVzdGltYXRlIG1vZGVsCmF1dG9fdHJlZSA8LSB0cmVlKG1wZyB+IGhvcnNlcG93ZXIsIGRhdGEgPSBBdXRvLAogICAgIGNvbnRyb2wgPSB0cmVlLmNvbnRyb2wobm9icyA9IG5yb3coQXV0byksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5kZXYgPSAwKSkKCm1vZCA8LSBwcnVuZS50cmVlKGF1dG9fdHJlZSwgYmVzdCA9IDIpCgojIHBsb3QgdHJlZQp0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQpwdHJlZSA8LSBnZ3Bsb3Qoc2VnbWVudCh0cmVlX2RhdGEpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWxfZnVsbCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoZGF0YSA9IGxlYWZfbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICB0aGVtZV9kZW5kcm8oKQoKIyBwbG90IHJlZ2lvbiBzcGFjZQpwcmVnIDwtIGdncGxvdChBdXRvLCBhZXMoaG9yc2Vwb3dlciwgbXBnKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuMikgKwogIGdlb21fc3RlcChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpLCBhZXMoeCwgeSksIHNpemUgPSAxLjUpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSBwYXJ0aXRpb24udHJlZS5kYXRhKG1vZCksIGFlcyh4aW50ZXJjZXB0ID0geCksIGxpbmV0eXBlID0gMikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyhtaW4oQXV0byRob3JzZXBvd2VyKSwgbWF4KEF1dG8kaG9yc2Vwb3dlcikpLAogICAgICAgICAgICAgICAgICB5bGltID0gYyhtaW4oQXV0byRtcGcpLCBtYXgoQXV0byRtcGcpKSwKICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSkpCgojIGRpc3BsYXkgcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShwdHJlZSwgcHJlZywgbmNvbCA9IDIsCiAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYihzdHJfYygiVGVybWluYWwgTm9kZXMgPSAiLCBjZWlsaW5nKGxlbmd0aChtb2QkZnJhbWUkeXZhbCkgLyAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAyMCkpKQpgYGAKCk9uIHRoZSBsZWZ0IGlzIHRoZSBkZWNpc2lvbiB0cmVlIGFmdGVyIHRoZSBmaXJzdCBpdGVyYXRpb24sIGFuZCBvbiB0aGUgcmlnaHQgaXMgdGhlIGRlY2lzaW9uIHRyZWUgZXN0aW1hdGlvbiBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaG9yc2Vwb3dlciBhbmQgaGlnaHdheSBtaWxlYWdlLiBUaGUgdHJlZSBjb25zaXN0cyBvZiB0aHJlZSBkaWZmZXJlbnQgY29tcG9uZW50czoKCiogRWFjaCBvdXRjb21lIGlzIGEgKip0ZXJtaW5hbCBub2RlKiogb3IgYSAqKmxlYWYqKgoqIFNwbGl0cyBvY2N1ciBhdCAqKmludGVybmFsIG5vZGVzKioKKiBUaGUgc2VnbWVudHMgY29ubmVjdGluZyBlYWNoIG5vZGUgYXJlIGNhbGxlZCAqKmJyYW5jaGVzKioKClRoaXMgbW9kZWwgaGFzIHR3byB0ZXJtaW5hbCBub2RlcyAoYHIgbW9kJGZyYW1lJHl2YWxbWzJdXWAgYW5kIGByIG1vZCRmcmFtZSR5dmFsW1szXV1gKSwgb25lIGludGVybmFsIG5vZGUgKGhvcnNlcG93ZXIgJGByIG1vZCRmcmFtZSRzcGxpdHNbMSwxXWAkKSwgYW5kIHR3byBicmFuY2hlcy4gRm9yIG9ic2VydmF0aW9ucyB3aXRoIGhvcnNlcG93ZXIgJGByIG1vZCRmcmFtZSRzcGxpdHNbMSwxXWAkLCB0aGUgbW9kZWwgZXN0aW1hdGVzIGhpZ2h3YXkgbWlsZWFnZSBvZiBgciBtb2QkZnJhbWUkeXZhbFtbMl1dYC4gRm9yIG9ic2VydmF0aW9ucyB3aXRoIGhvcnNlcG93ZXIgJGByIG1vZCRmcmFtZSRzcGxpdHNbMSwyXWAkLCB0aGUgbW9kZWwgZXN0aW1hdGVzIGhpZ2h3YXkgbWlsZWFnZSBvZiBgciBtb2QkZnJhbWUkeXZhbFtbM11dYC4gVGhlIHJlc3VsdGluZyByZWxhdGlvbnNoaXAgImN1cnZlIiAoc2VlIHJpZ2h0KSBsb29rcyBsaWtlIGEgc3RlcCBmdW5jdGlvbi4gRWFjaCBzZWdtZW50IG9mIHRoZSBmdW5jdGlvbiBpcyB0aGUgYG1lYW4oKWAgb2YgdGhlIG9ic2VydmF0aW9ucyBpbnNpZGUgdGhhdCByZWdpb24uCgpJZiB3ZSBwcm9jZWVkIHRvIHRoZSBuZXh0IGl0ZXJhdGlvbiwgdGhlIGRlY2lzaW9uIHRyZWUgc2VnbWVudHMgJFJfMSQgZnVydGhlci4KCmBgYHtyIGF1dG8tdHJlZTN9Cm1vZCA8LSBwcnVuZS50cmVlKGF1dG9fdHJlZSwgYmVzdCA9IDMpCgojIHBsb3QgdHJlZQp0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQpwdHJlZSA8LSBnZ3Bsb3Qoc2VnbWVudCh0cmVlX2RhdGEpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWxfZnVsbCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoZGF0YSA9IGxlYWZfbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICB0aGVtZV9kZW5kcm8oKQoKIyBwbG90IHJlZ2lvbiBzcGFjZQpwcmVnIDwtIGdncGxvdChBdXRvLCBhZXMoaG9yc2Vwb3dlciwgbXBnKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuMikgKwogIGdlb21fc3RlcChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpLCBhZXMoeCwgeSksIHNpemUgPSAxLjUpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSBwYXJ0aXRpb24udHJlZS5kYXRhKG1vZCksIGFlcyh4aW50ZXJjZXB0ID0geCksIGxpbmV0eXBlID0gMikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyhtaW4oQXV0byRob3JzZXBvd2VyKSwgbWF4KEF1dG8kaG9yc2Vwb3dlcikpLAogICAgICAgICAgICAgICAgICB5bGltID0gYyhtaW4oQXV0byRtcGcpLCBtYXgoQXV0byRtcGcpKSwKICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSkpCgojIGRpc3BsYXkgcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShwdHJlZSwgcHJlZywgbmNvbCA9IDIsCiAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYihzdHJfYygiVGVybWluYWwgTm9kZXMgPSAiLCBjZWlsaW5nKGxlbmd0aChtb2QkZnJhbWUkeXZhbCkgLyAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAyMCkpKQpgYGAKCk5vdyB0aGVyZSBhcmUgdGhyZWUgdGVybWluYWwgbm9kZXMgKGByIG1vZCRmcmFtZSR5dmFsWzM6NV1gKSwgdHdvIGludGVybmFsIG5vZGVzIChob3JzZXBvd2VyICRgciBtb2QkZnJhbWUkc3BsaXRzWzEsMV1gJCBhbmQgaG9yc2Vwb3dlciAkYHIgbW9kJGZyYW1lJHNwbGl0c1syLDFdYCQpLCBhbmQgdGhyZWUgYnJhbmNoZXMuIEludGVycHJldGluZyB0aGUgZGVjaXNpb24gdHJlZSBpcyBzdGlsbCByZWxhdGl2ZWx5IGludHVpdGl2ZToKCiogSWYgaG9yc2Vwb3dlciAkYHIgbW9kJGZyYW1lJHNwbGl0c1sxLDJdYCQsIHRoZW4gdGhlIG1vZGVsIGVzdGltYXRlcyBoaWdod2F5IG1pbGVhZ2UgdG8gYmUgYHIgbW9kJGZyYW1lJHl2YWxbNV1gLgoqIElmIGhvcnNlcG93ZXIgJGByIG1vZCRmcmFtZSRzcGxpdHNbMSwxXWAkLCB0aGVuIHdlIHByb2NlZWQgZG93biB0aGUgbGVmdCBicmFuY2ggdG8gdGhlIG5leHQgaW50ZXJuYWwgbm9kZS4KICAgICogSWYgaG9yc2Vwb3dlciAkYHIgbW9kJGZyYW1lJHNwbGl0c1syLDFdYCQsIHRoZW4gdGhlIG1vZGVsIGVzdGltYXRlcyBoaWdod2F5IG1pbGVhZ2UgdG8gYmUgYHIgbW9kJGZyYW1lJHl2YWxbM11gLgogICAgKiBJZiBob3JzZXBvd2VyICRgciBtb2QkZnJhbWUkc3BsaXRzWzIsMl1gJCwgdGhlbiB0aGUgbW9kZWwgZXN0aW1hdGVzIGhpZ2h3YXkgbWlsZWFnZSB0byBiZSBgciBtb2QkZnJhbWUkeXZhbFs0XWAuCgpJZiB3ZSBjb250aW51ZWQgdGhlIGl0ZXJhdGl2ZSBwcm9jZXNzIG1hbnkgbWFueSB0aW1lcywgd2UnZCBnZXQgYSBkZWNpc2lvbiB0cmVlIHRoYXQgbG9va3MgbGlrZSB0aGlzOgoKYGBge3IgYXV0by10cmVlYWxsfQptb2QgPC0gYXV0b190cmVlCgojIHBsb3QgdHJlZQp0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQpwdHJlZSA8LSBnZ3Bsb3Qoc2VnbWVudCh0cmVlX2RhdGEpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWxfZnVsbCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoZGF0YSA9IGxlYWZfbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICB0aGVtZV9kZW5kcm8oKQoKIyBwbG90IHJlZ2lvbiBzcGFjZQpwcmVnIDwtIGdncGxvdChBdXRvLCBhZXMoaG9yc2Vwb3dlciwgbXBnKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuMikgKwogIGdlb21fc3RlcChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpLCBhZXMoeCwgeSksIHNpemUgPSAxLjUpICsKICBnZW9tX3Ntb290aChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpLCBhZXMoeCwgeSksIHNlID0gRkFMU0UpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMobWluKEF1dG8kaG9yc2Vwb3dlciksIG1heChBdXRvJGhvcnNlcG93ZXIpKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMobWluKEF1dG8kbXBnKSwgbWF4KEF1dG8kbXBnKSksCiAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgc2l6ZSA9IDEpKQoKIyBkaXNwbGF5IHBsb3RzIHNpZGUgYnkgc2lkZQpncmlkLmFycmFuZ2UocHRyZWUsIHByZWcsIG5jb2wgPSAyLAogICAgICAgICAgICAgdG9wID0gdGV4dEdyb2Ioc3RyX2MoIlRlcm1pbmFsIE5vZGVzID0gIiwgY2VpbGluZyhsZW5ndGgobW9kJGZyYW1lJHl2YWwpIC8gMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplID0gMjApKSkKYGBgCgpUaGVyZSBhcmUgYHIgbnJvdyhtb2QkZnJhbWUpYCBub2RlcyAoaW50ZXJuYWwgYW5kIHRlcm1pbmFsKSBpbiB0aGlzIGRlY2lzaW9uIHRyZWUsIHdpdGggYHIgY2VpbGluZyhsZW5ndGgobW9kJGZyYW1lJHl2YWwpIC8gMilgIGRpZmZlcmVudCByZWdpb25zIGFuZCBgciBjZWlsaW5nKGxlbmd0aChtb2QkZnJhbWUkeXZhbCkgLyAyKWAgZGlmZmVyZW50IHByZWRpY3RlZCB2YWx1ZXMgZGVwZW5kaW5nIG9uIHRoZSBvYnNlcnZhdGlvbidzIHZhbHVlIGZvciBob3JzZXBvd2VyLiBOb3RpY2UgdGhvdWdoIHRoYXQgdGhlIHN0ZXAgZnVuY3Rpb24gYWN0dWFsbHkgbG9va3Mgc2ltaWxhciB0byBhIHF1YWRyYXRpYyBzbW9vdGhpbmcgbGluZSwgbWF0Y2hpbmcgb3VyIGV4cGVjdGF0aW9ucyBvZiB0aGUgcmVsYXRpb25zaGlwLiBJbiBmYWN0LCBjb21wYXJlZCB0byB0aGUgbGluZWFyIG1vZGVsIChgciBtc2UoYXV0b19sbSwgQXV0bylgKSB0aGUgZGVjaXNpb24gdHJlZSBnZW5lcmF0ZXMgYSBmYXIgbG93ZXIgdHJhaW5pbmcgTVNFIChgciBtc2UoYXV0b190cmVlLCBBdXRvKWApLl5bWWVzLCB3ZSBrbm93IFt0aGUgcGl0ZmFsbHMgb2YgdXNpbmcgdHJhaW5pbmcgTVNFIGZvciBtb2RlbCBjb21wYXJpc29uXShwZXJzcDAwNl9yZXNhbXBsaW5nLmh0bWwjdHJhaW5pbmdfdnNfdGVzdF9kYXRhKS4gSXQncyBqdXN0IGFuIGV4YW1wbGUgYmVjYXVzZSB3ZSBoYXZlbid0IHNwbGl0IHRoZSBkYXRhIGludG8gYSB2YWxpZGF0aW9uIHNldC5dCgojIyMgTXVsdGlwbGUgcHJlZGljdG9ycwoKV2l0aCBqdXN0IGEgc2luZ2xlIHByZWRpY3RvciwgdGhlIHJlZ2lvbnMgYXJlIGEgZnVuY3Rpb24gb2YgdGhhdCBvbmUgcHJlZGljdG9yLiBJZiB3ZSBhZGQgYSBzZWNvbmQgcHJlZGljdG9yIChzYXksIHZlaGljbGUgd2VpZ2h0KSwgdGhlIHJlZ2lvbnMgYmVjb21lIGEgZnVuY3Rpb24gb2YgKipib3RoKiogcHJlZGljdG9ycyBhbmQgY2FuIGJlIHZpc3VhbGl6ZWQgYXMgZ3JpZHMgb3IgYm94ZXMuCgpgYGB7ciBhdXRvLXRyZWUtd2VpZ2h0fQphdXRvX3RyZWUgPC0gdHJlZShtcGcgfiBob3JzZXBvd2VyICsgd2VpZ2h0LCBkYXRhID0gQXV0bywKICAgICBjb250cm9sID0gdHJlZS5jb250cm9sKG5vYnMgPSBucm93KEF1dG8pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluZGV2ID0gMCkpCgptb2QgPC0gcHJ1bmUudHJlZShhdXRvX3RyZWUsIGJlc3QgPSAzKQoKIyBwbG90IHRyZWUKdHJlZV9kYXRhIDwtIGRlbmRyb19kYXRhKG1vZCkKcHRyZWUgPC0gZ2dwbG90KHNlZ21lbnQodHJlZV9kYXRhKSkgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGxhYmVsKHRyZWVfZGF0YSksIAogICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsX2Z1bGwpLCB2anVzdCA9IC0wLjUsIHNpemUgPSAzKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBsZWFmX2xhYmVsKHRyZWVfZGF0YSksIAogICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSwgdmp1c3QgPSAwLjUsIHNpemUgPSAzKSArCiAgdGhlbWVfZGVuZHJvKCkKCiMgcGxvdCByZWdpb24gc3BhY2UKcHJlZyA8LSBnZ3Bsb3QoQXV0bywgYWVzKHdlaWdodCwgaG9yc2Vwb3dlcikpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjIpICsKICBnZW9tX3NlZ21lbnQoZGF0YSA9IHBhcnRpdGlvbi50cmVlLmRhdGEobW9kKVtbMV1dLAogICAgICAgICAgICAgICBhZXMoeCA9IHhtaW4sIHhlbmQgPSB4bWF4LCB5ID0geW1pbiwgeWVuZCA9IHltYXgpKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBwYXJ0aXRpb24udHJlZS5kYXRhKG1vZClbWzJdXSwKICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwgbGFiZWwgPSBsYWJlbCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMobWluKEF1dG8kd2VpZ2h0KSwgbWF4KEF1dG8kd2VpZ2h0KSksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKG1pbihBdXRvJGhvcnNlcG93ZXIpLCBtYXgoQXV0byRob3JzZXBvd2VyKSksCiAgICAgICAgICAgICAgICAgIGV4cGFuZCA9IEZBTFNFKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgc2l6ZSA9IDEpKQoKIyBkaXNwbGF5IHBsb3RzIHNpZGUgYnkgc2lkZQpncmlkLmFycmFuZ2UocHRyZWUsIHByZWcsIG5jb2wgPSAyLAogICAgICAgICAgICAgdG9wID0gdGV4dEdyb2Ioc3RyX2MoIlRlcm1pbmFsIE5vZGVzID0gIiwgY2VpbGluZyhsZW5ndGgobW9kJGZyYW1lJHl2YWwpIC8gMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplID0gMjApKSkKYGBgCgoqIElmIHdlaWdodCAkYHIgbW9kJGZyYW1lJHNwbGl0c1sxLDJdYCQsIHRoZW4gdGhlIG1vZGVsIGVzdGltYXRlcyBoaWdod2F5IG1pbGVhZ2UgdG8gYmUgYXBwcm94aW1hdGVseSBgciBtb2QkZnJhbWUkeXZhbFs1XWAuCiogSWYgd2VpZ2h0ICRgciBtb2QkZnJhbWUkc3BsaXRzWzEsMV1gJCwgdGhlbiB3ZSBwcm9jZWVkIGRvd24gdGhlIGxlZnQgYnJhbmNoIHRvIHRoZSBuZXh0IGludGVybmFsIG5vZGUuCiAgICAqIElmIGhvcnNlcG93ZXIgJGByIG1vZCRmcmFtZSRzcGxpdHNbMiwxXWAkLCB0aGVuIHRoZSBtb2RlbCBlc3RpbWF0ZXMgaGlnaHdheSBtaWxlYWdlIHRvIGJlIGByIG1vZCRmcmFtZSR5dmFsWzNdYC4KICAgICogSWYgaG9yc2Vwb3dlciAkYHIgbW9kJGZyYW1lJHNwbGl0c1syLDJdYCQsIHRoZW4gdGhlIG1vZGVsIGVzdGltYXRlcyBoaWdod2F5IG1pbGVhZ2UgdG8gYmUgYHIgbW9kJGZyYW1lJHl2YWxbNF1gLgoKV2UgY2FuIGNvbnRpbnVlIHRvIGJ1aWxkIHRoZSB0cmVlIHVwIGJ5IGFkZGluZyBhZGRpdGlvbmFsIG5vZGVzOgoKYGBge3IgYXV0by10cmVlLXdlaWdodC1pfQpmb3IoaSBpbiBjKDQ6MTAsIDIwLCA1MCkpewogIG1vZCA8LSBwcnVuZS50cmVlKGF1dG9fdHJlZSwgYmVzdCA9IGkpCiAgCiAgIyBwbG90IHRyZWUKICB0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQogIHB0cmVlIDwtIGdncGxvdChzZWdtZW50KHRyZWVfZGF0YSkpICsKICAgIGdlb21fc2VnbWVudChhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpLCAKICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogICAgZ2VvbV90ZXh0KGRhdGEgPSBsYWJlbCh0cmVlX2RhdGEpLCAKICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwgbGFiZWwgPSBsYWJlbF9mdWxsKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKwogICAgZ2VvbV90ZXh0KGRhdGEgPSBsZWFmX2xhYmVsKHRyZWVfZGF0YSksIAogICAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICAgIHRoZW1lX2RlbmRybygpCiAgCiAgIyBwbG90IHJlZ2lvbiBzcGFjZQogIHByZWcgPC0gZ2dwbG90KEF1dG8sIGFlcyh3ZWlnaHQsIGhvcnNlcG93ZXIpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gLjIpICsKICAgIGdlb21fc2VnbWVudChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpW1sxXV0sCiAgICAgICAgICAgICAgICAgYWVzKHggPSB4bWluLCB4ZW5kID0geG1heCwgeSA9IHltaW4sIHllbmQgPSB5bWF4KSkgKwogICAgZ2VvbV90ZXh0KGRhdGEgPSBwYXJ0aXRpb24udHJlZS5kYXRhKG1vZClbWzJdXSwKICAgICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSkgKwogICAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKG1pbihBdXRvJHdlaWdodCksIG1heChBdXRvJHdlaWdodCkpLAogICAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKG1pbihBdXRvJGhvcnNlcG93ZXIpLCBtYXgoQXV0byRob3JzZXBvd2VyKSksCiAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEsIHNpemUgPSAxKSkKICAKICAjIGRpc3BsYXkgcGxvdHMgc2lkZSBieSBzaWRlCiAgZ3JpZC5hcnJhbmdlKHB0cmVlLCBwcmVnLCBuY29sID0gMiwKICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2Ioc3RyX2MoIlRlcm1pbmFsIE5vZGVzID0gIiwgaSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdwID0gZ3Bhcihmb250c2l6ZSA9IDIwKSkpIAp9CmBgYAoKIyMjIEVzdGltYXRpb24gcHJvY2VkdXJlCgpXZSBoYXZlIGFscmVhZHkgaWRlbnRpZmllZCB0aGF0IGRlY2lzaW9uIHRyZWVzIHVzZSBzdHJhdGlmaWNhdGlvbiB0byBkaXZpZGUgdGhlIG9ic2VydmF0aW9ucyBpbnRvICRSX0okIHJlZ2lvbnMuIExpa2UgaW4gbGluZWFyIHJlZ3Jlc3Npb24sIG91ciBnb2FsIGlzIHRvIG1pbmltaXplIHRoZSByZXNpZHVhbCBzdW0gb2YgdGhlIHNxdWFyZWQgZXJyb3JzIChSU1MpLCBkZWZpbmVkIGZvciBhIGRlY2lzaW9uIHRyZWUgYXM6CgokJFxzdW1fe2o9MX1eSiBcc3VtX3tpIFxpbiBSX2p9ICh5X2kgLSBcaGF0e3l9X3tSX2p9KV4yJCQKCndoZXJlICRcaGF0e3l9X3tSX2p9JCBpcyB0aGUgbWVhbiByZXNwb25zZSBmb3IgdGhlIG9ic2VydmF0aW9ucyBpbiB0aGUgJGokdGggcmVnaW9uLiBJbiBvcmRlciB0byBkbyB0aGlzLCBkZWNpc2lvbiB0cmVlcyBpbXBsZW1lbnQgYSAqKnJlY3Vyc2l2ZSBiaW5hcnkgc3RyYXRlZ3kqKi4gVGhlIHByb2Nlc3MgYmVnaW5zIGF0IHRoZSB0b3Agb2YgdGhlIHRyZWUgKCoqdG9wLWRvd24qKikgYW5kIHN1Y2Nlc3NpdmVseSBzcGxpdHMgdGhlIGRhdGEgaW50byBhIG5ldyByZWdpb24uIFRoaXMgc3BsaXQgZ2VuZXJhdGVzIHR3byBuZXcgYnJhbmNoZXMgaW4gdGhlIHRyZWUuIFJhdGhlciB0aGFuIGxvb2tpbmcgZm9yd2FyZCB0byBzZWxlY3QgdGhlIG9wdGltYWwgc3BsaXQgYW1vbmcgYWxsIGZ1dHVyZSBwb3NzaWJpbGl0aWVzLCB0aGlzIGFwcHJvYWNoIGlzICoqZ3JlZWR5KiogaW4gdGhhdCBpdCBzZWxlY3RzIHRoZSBiZXN0IHNwbGl0ICoqYXQgdGhhdCBwYXJ0aWN1bGFyIHN0ZXAqKi4gR2l2ZW4gYWxsIHRoZSBwb3RlbnRpYWwgc3BsaXRzIHRoYXQgY291bGQgYmUgcGVyZm9ybWVkIG9uIG9uZSBvZiB0aGUgcHJlZGljdG9ycyAkWF8xLCBYXzIsIFxkb3RzLCBYX3AkIHByZWRpY3RvcnMsIHRoZSBhbGdvcml0aG0gYXNzaWducyBhIGN1dHBvaW50ICRzJCB0aGF0IHNwbGl0cyB0aGUgZGF0YSBpbiB0aGUgbWFubmVyIHRoYXQgcmVkdWNlcyB0aGUgUlNTIGJ5IHRoZSBsYXJnZXN0IGFtb3VudC4gQXMgdGhlIG51bWJlciBvZiBwcmVkaWN0b3JzICRwJCBhbmQgb2JzZXJ2YXRpb25zICROJCBpbmNyZWFzZXMsIHRoZSBtb3JlIHBvdGVudGlhbCBjdXRwb2ludHMgdGhlIGFsZ29yaXRobSBtdXN0IGNvbnNpZGVyLiBIb3dldmVyIGV2ZW4gd2l0aCByZWxhdGl2ZWx5IGxhcmdlIG51bWJlcnMgb2YgcHJlZGljdG9ycyBhbmQgb2JzZXJ2YXRpb25zLCB0aGUgY29tcHV0YXRpb25hbCBwcm9jZXNzIGlzIHF1aXRlIGVmZmljaWVudC4KClRoaXMgcHJvY2VzcyBjb250aW51ZXMgdW50aWwgc29tZSBkZXNpZ25hdGVkIHN0b3BwaW5nIGNyaXRlcmlhIGlzIHJlYWNoZWQsIG90aGVyd2lzZSBpdCBjb3VsZCBjb250aW51ZSB1bnRpbCBlYWNoIHRyYWluaW5nIG9ic2VydmF0aW9uIGlzIHNvcnRlZCBpbnRvIGl0cyBvd24gbm9kZSAoaS5lLiBvdmVyZml0dGluZykuIEZvciBleGFtcGxlLCBieSBkZWZhdWx0IHRoZSBgdHJlZSgpYCBmdW5jdGlvbiBpbiBSIChmcm9tIHRoZSBgdHJlZWAgbGlicmFyeSkgd2lsbCBub3Qgc3BsaXQgYSBub2RlIGlmIGEgcmVzdWx0aW5nIG5vZGUgd291bGQgY29udGFpbiBmZXdlciB0aGFuIDEwIHRyYWluaW5nIG9ic2VydmF0aW9ucy4gT25jZSB0aGlzIGl0ZXJhdGl2ZSBwcm9jZXNzIHN0b3BzLCB3ZSBjYW4gZ2VuZXJhdGUgcHJlZGljdGVkIHZhbHVlcyBmb3IgdGhlIHJlc3BvbnNlIG9mIGEgZ2l2ZW4gdGVzdCBvYnNlcnZhdGlvbiBieSBjYWxjdWxhdGluZyB0aGUgbWVhbiBvZiB0aGUgdHJhaW5pbmcgb2JzZXJ2YXRpb25zIGZvciB0aGUgcmVnaW9uIGluIHdoaWNoIHRoZSB0ZXN0IG9ic2VydmF0aW9uIGJlbG9uZ3MuCgojIyMgUHJ1bmluZyB0aGUgdHJlZQoKTm90aWNlIHRoYXQgd2Ugc3RvcCBzcGxpdHRpbmcgdGhlIHRyZWUgaW4gb3JkZXIgdG8gcHJldmVudCBvdmVyZml0dGluZy4gRXZlbiB3aXRoIHRoZSBhYm92ZSBwcm9jZXNzLCBkZWNpc2lvbiB0cmVlcyBhcmUgaGlnaGx5IHN1c2NlcHRpYmxlIHRvIG92ZXJmaXR0aW5nIGR1ZSB0byBpdHMgbmF0dXJhbCBjb21wbGV4aXR5LiBBbmQgaWYgd2Ugc2ltcGx5IHNldCB0aGUgc3RvcHBpbmcgY3JpdGVyaWEgYXQgYSBoaWdoZXIgbGV2ZWwsIHdlIG1heSBtaXNzIGNydWNpYWwgYnJhbmNoZXMgbGF0ZXIgb24gaW4gdGhlIHByb2Nlc3MuIEluc3RlYWQgd2Ugd2FudCBhIG1ldGhvZCB0aGF0IGFsbG93cyB1cyB0byBncm93IGEgbGFyZ2UgdHJlZSBhbmQgcHJlc2VydmUgdGhlIG1vc3QgaW1wb3J0YW50IGJyYW5jaGVzIG9yIGVsZW1lbnRzLgoKIVtdKGh0dHBzOi8vZ3Jvd2luZ3RvZ2V0aGVyLmFyZWF2b2ljZXMuY29tL2ZpbGVzLzIwMTUvMTEvcHJ1bmluZy5qcGcpCgpJbiBlc3NlbmNlLCB3ZSB3YW50IHRvICoqcHJ1bmUqKiB0aGUgdHJlZS4gKipDb3N0IGNvbXBsZXhpdHkgcHJ1bmluZyoqIGlzIG9uZSBwcmVkb21pbmFudCBtZXRob2QgZm9yIGFjaGlldmluZyB0aGlzIGdvYWwuIFdoaWxlIEkgbGVhdmUgdGhlIG1hdGhlbWF0aWNzIG9mIHRoaXMgb3BlcmF0aW9uIHRvIFtJU0xdKGh0dHBzOi8vbGluay1zcHJpbmdlci1jb20ucHJveHkudWNoaWNhZ28uZWR1L2Jvb2svMTAuMTAwNyUyRjk3OC0xLTQ2MTQtNzEzOC03KSwgY29zdCBjb21wbGV4aXR5IHBydW5pbmcgdXNlcyBhICoqdHVuaW5nIHBhcmFtZXRlcioqIHRvIHNlbGVjdGl2ZWx5IHBydW5lIG9yIHNuaXAgYnJhbmNoZXMgdGhhdCBkbyBub3QgY29udHJpYnV0ZSBzaWduaWZpY2FudCBwcmVkaWN0aXZlIGFjY3VyYWN5LCByZXN1bHRpbmcgaW4gYSBzdWJ0cmVlIGdlbmVyYXRlZCBmcm9tIHRoZSBmdWxsIHRyZWUuIERpZmZlcmVudCB0dW5pbmcgcGFyYW1ldGVyIHZhbHVlcyB3aWxsIGxlYWQgdG8gZGlmZmVyZW50IHRyYWRlLW9mZnMgYmV0d2VlbiBtb2RlbCBjb21wbGV4aXR5IGFuZCBtb2RlbCBhY2N1cmFjeSwgc28gd2UgY2FuIHVzZSBmdW5jdGlvbiBzdWNoIGFzIGBwcnVuZS50cmVlKClgIGluIGNvbmp1bmN0aW9uIHdpdGggWyRrJC1mb2xkIGNyb3NzLXZhbGlkYXRpb25dKHBlcnNwMDA2X3Jlc2FtcGxpbmcuaHRtbCNrLWZvbGRfY3Jvc3MtdmFsaWRhdGlvbikgdG8gc2VsZWN0IGEgY29zdCBjb21wbGV4aXR5IHBhcmFtZXRlciB0aGF0IG9wdGltYWxseSBiYWxhbmNlcyB0aGUgdHJhZGUtb2ZmIGZvciB0aGUgc3BlY2lmaWMgZGF0YXNldC5eW0FsdGVybmF0aXZlbHksIHlvdSBzcGVjaWZ5IHRoZSB0b3RhbCBudW1iZXIgb2YgdGVybWluYWwgbm9kZXMgdG8ga2VlcCBpbiB0aGUgbW9kZWwgYW5kIHNlbGVjdCB0aGF0IHZhbHVlIHRvIG9wdGltYWxseSBiYWxhbmNlIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiBjb21wbGV4aXR5IGFuZCBhY2N1cmFjeS5dCgpGb3IgZXhhbXBsZSwgaGVyZSBpcyB0aGUgZnVsbCB0cmVlIGdyb3duIGZvciB0aGUgYGhvcnNlcG93ZXIgKyB3ZWlnaHRgIGRlY2lzaW9uIHRyZWU6CgpgYGB7ciBhdXRvLXRyZWUtZGVmYXVsdH0KYXV0b190cmVlIDwtIHRyZWUobXBnIH4gaG9yc2Vwb3dlciArIHdlaWdodCwgZGF0YSA9IEF1dG8sCiAgICAgY29udHJvbCA9IHRyZWUuY29udHJvbChub2JzID0gbnJvdyhBdXRvKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmRldiA9IDApKQptb2QgPC0gYXV0b190cmVlCgojIHBsb3QgdHJlZQp0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQpwdHJlZSA8LSBnZ3Bsb3Qoc2VnbWVudCh0cmVlX2RhdGEpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWxfZnVsbCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoZGF0YSA9IGxlYWZfbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICB0aGVtZV9kZW5kcm8oKQoKIyBwbG90IHJlZ2lvbiBzcGFjZQpwcmVnIDwtIGdncGxvdChBdXRvLCBhZXMod2VpZ2h0LCBob3JzZXBvd2VyKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuMikgKwogIGdlb21fc2VnbWVudChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpW1sxXV0sCiAgICAgICAgICAgICAgIGFlcyh4ID0geG1pbiwgeGVuZCA9IHhtYXgsIHkgPSB5bWluLCB5ZW5kID0geW1heCkpICsKICBnZW9tX3RleHQoZGF0YSA9IHBhcnRpdGlvbi50cmVlLmRhdGEobW9kKVtbMl1dLAogICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyhtaW4oQXV0byR3ZWlnaHQpLCBtYXgoQXV0byR3ZWlnaHQpKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMobWluKEF1dG8kaG9yc2Vwb3dlciksIG1heChBdXRvJGhvcnNlcG93ZXIpKSwKICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSkpCgojIGRpc3BsYXkgcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShwdHJlZSwgcHJlZywgbmNvbCA9IDIsCiAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYihzdHJfYygiVGVybWluYWwgTm9kZXMgPSAiLCBjZWlsaW5nKGxlbmd0aChtb2QkZnJhbWUkeXZhbCkgLyAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAyMCkpKQpgYGAKCkxldCdzIHVzZSAkMTAkLWZvbGQgQ1YgdG8gc2VsZWN0IHRoZSBvcHRpbWFsIHRyZWUgc2l6ZToKCmBgYHtyIGF1dG8tdHJlZS1kZWZhdWx0LXBydW5lfQojIGdlbmVyYXRlIDEwLWZvbGQgQ1YgdHJlZXMKYXV0b19jdiA8LSBjcm9zc3Zfa2ZvbGQoQXV0bywgayA9IDEwKSAlPiUKICBtdXRhdGUodHJlZSA9IG1hcCh0cmFpbiwgfiB0cmVlKG1wZyB+IGhvcnNlcG93ZXIgKyB3ZWlnaHQsIGRhdGEgPSAuLAogICAgIGNvbnRyb2wgPSB0cmVlLmNvbnRyb2wobm9icyA9IG5yb3coQXV0byksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5kZXYgPSAwKSkpKQoKIyBjYWxjdWxhdGUgZWFjaCBwb3NzaWJsZSBwcnVuZSByZXN1bHQgZm9yIGVhY2ggZm9sZAphdXRvX2N2IDwtIGV4cGFuZC5ncmlkKGF1dG9fY3YkLmlkLCAyOjEwKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBtdXRhdGUoVmFyMiA9IGFzLm51bWVyaWMoVmFyMikpICU+JQogIHJlbmFtZSguaWQgPSBWYXIxLAogICAgICAgICBrID0gVmFyMikgJT4lCiAgbGVmdF9qb2luKGF1dG9fY3YpICU+JQogIG11dGF0ZShwcnVuZSA9IG1hcDIodHJlZSwgaywgfiBwcnVuZS50cmVlKC54LCBiZXN0ID0gLnkpKSwKICAgICAgICAgbXNlID0gbWFwMl9kYmwocHJ1bmUsIHRlc3QsIG1zZSkpCgphdXRvX2N2ICU+JQogIHNlbGVjdChrLCBtc2UpICU+JQogIGdyb3VwX2J5KGspICU+JQogIHN1bW1hcml6ZSh0ZXN0X21zZSA9IG1lYW4obXNlKSwKICAgICAgICAgICAgc2QgPSBzZChtc2UsIG5hLnJtID0gVFJVRSkpICU+JQogIGdncGxvdChhZXMoaywgdGVzdF9tc2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4ID0gIk51bWJlciBvZiB0ZXJtaW5hbCBub2RlcyIsCiAgICAgICB5ID0gIlRlc3QgTVNFIikKYGBgCgpUaGUgbWluaW11bSBjcm9zcy12YWxpZGF0ZWQgdGVzdCBNU0UgaXMgZm9yIDcgdGVybWluYWwgbm9kZXMuIEhlcmUncyB3aGF0IHRoYXQgdHJlZSBsb29rcyBsaWtlOgoKYGBge3IgYXV0by10cmVlLTd9Cm1vZCA8LSBwcnVuZS50cmVlKGF1dG9fdHJlZSwgYmVzdCA9IDcpCgojIHBsb3QgdHJlZQp0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQpwdHJlZSA8LSBnZ3Bsb3Qoc2VnbWVudCh0cmVlX2RhdGEpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWxfZnVsbCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoZGF0YSA9IGxlYWZfbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICB0aGVtZV9kZW5kcm8oKQoKIyBwbG90IHJlZ2lvbiBzcGFjZQpwcmVnIDwtIGdncGxvdChBdXRvLCBhZXMod2VpZ2h0LCBob3JzZXBvd2VyKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAuMikgKwogIGdlb21fc2VnbWVudChkYXRhID0gcGFydGl0aW9uLnRyZWUuZGF0YShtb2QpW1sxXV0sCiAgICAgICAgICAgICAgIGFlcyh4ID0geG1pbiwgeGVuZCA9IHhtYXgsIHkgPSB5bWluLCB5ZW5kID0geW1heCkpICsKICBnZW9tX3RleHQoZGF0YSA9IHBhcnRpdGlvbi50cmVlLmRhdGEobW9kKVtbMl1dLAogICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYyhtaW4oQXV0byR3ZWlnaHQpLCBtYXgoQXV0byR3ZWlnaHQpKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMobWluKEF1dG8kaG9yc2Vwb3dlciksIG1heChBdXRvJGhvcnNlcG93ZXIpKSwKICAgICAgICAgICAgICAgICAgZXhwYW5kID0gRkFMU0UpICsKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBzaXplID0gMSkpCgojIGRpc3BsYXkgcGxvdHMgc2lkZSBieSBzaWRlCmdyaWQuYXJyYW5nZShwdHJlZSwgcHJlZywgbmNvbCA9IDIsCiAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYihzdHJfYygiVGVybWluYWwgTm9kZXMgPSAiLCBjZWlsaW5nKGxlbmd0aChtb2QkZnJhbWUkeXZhbCkgLyAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAyMCkpKQpgYGAKCiMjIENsYXNzaWZpY2F0aW9uIHRyZWVzCgpBICoqY2xhc3NpZmljYXRpb24gdHJlZSoqIGlzIHNpbWlsYXIgdG8gYSByZWdyZXNzaW9uIHRyZWUsIGV4Y2VwdCB0aGF0IHRoZSByZXNwb25zZSB2YXJpYWJsZSBpcyBxdWFsaXRhdGl2ZS4gSW4gbWFraW5nIHByZWRpY3Rpb25zLCB3ZSB3b3VsZCBwcmVkaWN0IGZvciBhIHRlc3Qgc2V0IG9ic2VydmF0aW9uIHRoZSBtb3N0IGNvbW1vbmx5IG9jY3VycmluZyBjbGFzcyB2YWx1ZSBpbiB0aGUgZ2l2ZW4gcmVnaW9uLiBIb3dldmVyIHdlIHdpbGwgYWxzbyBjb25zaWRlciB0aGUgKipjbGFzcyBwcm9wb3J0aW9ucyoqLCBvciB0aGUgcHJvcG9ydGlvbiBvZiB0cmFpbmluZyBvYnNlcnZhdGlvbnMgaW4gdGhlIHJlZ2lvbiAkUl9qJCB0aGF0IGZhbGwgaW50byBhIGdpdmVuIGNsYXNzLgoKUmF0aGVyIHRoYW4gdXNpbmcgUlNTIHRvIGdyb3cgdGhlIHRyZWUsIHdlIGhhdmUgdGhyZWUgb3B0aW9ucyBmb3IgbWluaW1pemluZyBlcnJvci4gQW4gb2J2aW91cyBjaG9pY2UgbWlnaHQgYmUgdGhlICoqY2xhc3NpZmljYXRpb24gZXJyb3IgcmF0ZSoqLCBvciB0aGUgcHJvcG9ydGlvbiBvZiB0cmFpbmluZyBvYnNlcnZhdGlvbnMgaW4gYSBnaXZlbiByZWdpb24gdGhhdCBkbyBub3QgYmVsb25nIHRvIHRoZSBtb3N0IGNvbW1vbiBjbGFzczoKCiQkRSA9IDEgLSBcbWF4X3trfShcaGF0e3B9X3tta30pJCQKCndoZXJlICRcaGF0e3B9X3tta30kIGlzIHRoZSBwcm9wb3J0aW9uIG9mIHRyYWluaW5nIG9ic2VydmF0aW9ucyBpbiByZWdpb24gJG0kIHRoYXQgZG8gbm90IGJlbG9uZyB0byB0aGUgbW9zdCBjb21tb24gY2xhc3MgJGskLgoKSW4gcHJhY3RpY2UsIHR3byBvdGhlciBtZXRob2RzIGdyb3cgYmV0dGVyIGFuZCBtb3JlIGFjY3VyYXRlIHRyZWVzLiBUaGUgKipHaW5pIGluZGV4KiogaXMgZGVmaW5lZCBhczoKCiQkRyA9IFxzdW1fe2sgPSAxfV5rIFxoYXR7cH1fe21rfSAoMSAtIFxoYXR7cH1fe21rfSkkJAoKYW5kIGlzIGEgbWVhc3VyZSBvZiBub2RlICoqcHVyaXR5KiouIFRoZSBoaWdoZXIgdGhlIHByb3BvcnRpb24gb2Ygb2JzZXJ2YXRpb25zIGJlbG9uZ2luZyB0byBhIHNpbmdsZSBjbGFzcywgdGhlIGNsb3NlciB0aGlzIHZhbHVlIHdpbGwgYmUgdG8gMC4KClRoZSBhbHRlcm5hdGl2ZSBpcyAqKmNyb3NzLWVudHJvcHkqKjoKCiQkRCA9IC0gXHN1bV97ayA9IDF9XksgXGhhdHtwfV97bWt9IFxsb2coXGhhdHtwfV97bWt9KSQkCgpBcyBtb3JlIG9ic2VydmF0aW9ucyBhcmUgY2xvc2VyIHRvIG9yIG5lYXIgMCBvciAxLCBjcm9zcy1lbnRyb3B5IHdpbGwgc2hyaW5rIHRvd2FyZHMgemVyby4gU28gZm9yIGNsYXNzaWZpY2F0aW9uIHRyZWVzLCBlYWNoIHNwbGl0IGNhbiBiZSBldmFsdWF0ZWQgdXNpbmcgb25lIG9mIHRoZXNlIGNyaXRlcmlhLCB0aG91Z2ggYWdhaW4gaXQgaXMgdHlwaWNhbGx5IHRoZSBHaW5pIGluZGV4IG9yIGNyb3NzLWVudHJvcHkuXltgdHJlZSgpYCB1c2VzIHRoZSBHaW5pIGluZGV4IGZvciBjbGFzc2lmaWNhdGlvbiB0cmVlcy5dCgpMZXQncyByZXR1cm4gdG8gb3VyIHJ1bm5pbmcgVGl0YW5pYyBleGFtcGxlLiBJIHdhbnQgdG8gcHJlZGljdCB3aG8gbGl2ZXMgYW5kIHdobyBkaWVzIGR1cmluZyB0aGlzIGV2ZW50LiBJbnN0ZWFkIG9mIHVzaW5nIFtsb2dpc3RpYyByZWdyZXNzaW9uXShwZXJzcDAwNF9sb2dpc3RpY19yZWdyZXNzaW9uLmh0bWwpLCBJJ20gZ29pbmcgdG8gY2FsY3VsYXRlIGEgZGVjaXNpb24gdHJlZSBiYXNlZCBvbiBhIHBhc3NlbmdlcidzIGFnZSBhbmQgZ2VuZGVyLiBIZXJlJ3Mgd2hhdCB0aGF0IGRlY2lzaW9uIHRyZWUgbG9va3MgbGlrZToKCmBgYHtyIHRpdGFuaWNfdHJlZX0KdGl0YW5pYyA8LSB0aXRhbmljX3RyYWluICU+JQogIGFzX3RpYmJsZSgpICU+JQogIG11dGF0ZShTdXJ2aXZlZCA9IGZhY3RvcihTdXJ2aXZlZCwgbGV2ZWxzID0gMDoxLCBsYWJlbHMgPSBjKCJEaWVkIiwgIlN1cnZpdmVkIikpLAogICAgICAgICBGZW1hbGUgPSBmYWN0b3IoU2V4LCBsZXZlbHMgPSBjKCJtYWxlIiwgImZlbWFsZSIpKSkKCiMgZXN0aW1hdGUgbW9kZWwKdGl0YW5pY190cmVlIDwtIHRyZWUoU3Vydml2ZWQgfiBBZ2UgKyBGZW1hbGUsIGRhdGEgPSB0aXRhbmljLAogICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gdHJlZS5jb250cm9sKG5vYnMgPSBucm93KHRpdGFuaWMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluZGV2ID0gLjAwMSkpCgojIHBsb3QgdW5wcnVuZWQgdHJlZQptb2QgPC0gdGl0YW5pY190cmVlCgp0cmVlX2RhdGEgPC0gZGVuZHJvX2RhdGEobW9kKQpnZ3Bsb3Qoc2VnbWVudCh0cmVlX2RhdGEpKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCksIAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fdGV4dChkYXRhID0gbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWxfZnVsbCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3RleHQoZGF0YSA9IGxlYWZfbGFiZWwodHJlZV9kYXRhKSwgCiAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGxhYmVsID0gbGFiZWwpLCB2anVzdCA9IDAuNSwgc2l6ZSA9IDMpICsKICB0aGVtZV9kZW5kcm8oKSArCiAgbGFicyh0aXRsZSA9ICJUaXRhbmljIHN1cnZpdmFsIHRyZWUiLAogICAgICAgc3VidGl0bGUgPSAiQWdlICsgR2VuZGVyIikKYGBgCgpgYGB7ciB0aXRhbmljLXRyZWUtcHJ1bmUsIGRlcGVuZHNvbj0idGl0YW5pYy10cmVlIn0KZXJyLnJhdGUudHJlZSA8LSBmdW5jdGlvbihtb2RlbCwgZGF0YSkgewogIGRhdGEgPC0gYXNfdGliYmxlKGRhdGEpCiAgcmVzcG9uc2UgPC0gYXMuY2hhcmFjdGVyKG1vZGVsJHRlcm1zW1syXV0pCiAgCiAgcHJlZCA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gZGF0YSwgdHlwZSA9ICJjbGFzcyIpCiAgYWN0dWFsIDwtIGRhdGFbW3Jlc3BvbnNlXV0KICAKICByZXR1cm4obWVhbihwcmVkICE9IGFjdHVhbCwgbmEucm0gPSBUUlVFKSkKfQoKIyBnZW5lcmF0ZSAxMC1mb2xkIENWIHRyZWVzCnRpdGFuaWNfY3YgPC0gdGl0YW5pYyAlPiUKICBuYS5vbWl0KCkgJT4lCiAgY3Jvc3N2X2tmb2xkKGsgPSAxMCkgJT4lCiAgbXV0YXRlKHRyZWUgPSBtYXAodHJhaW4sIH4gdHJlZShTdXJ2aXZlZCB+IEFnZSArIEZlbWFsZSwgZGF0YSA9IC4sCiAgICAgY29udHJvbCA9IHRyZWUuY29udHJvbChub2JzID0gbnJvdyh0aXRhbmljKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbmRldiA9IC4wMDEpKSkpCgojIGNhbGN1bGF0ZSBlYWNoIHBvc3NpYmxlIHBydW5lIHJlc3VsdCBmb3IgZWFjaCBmb2xkCnRpdGFuaWNfY3YgPC0gZXhwYW5kLmdyaWQodGl0YW5pY19jdiQuaWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxKGZyb20gPSAyLCB0byA9IGNlaWxpbmcobGVuZ3RoKG1vZCRmcmFtZSR5dmFsKSAvIDIpKSkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgbXV0YXRlKFZhcjIgPSBhcy5udW1lcmljKFZhcjIpKSAlPiUKICByZW5hbWUoLmlkID0gVmFyMSwKICAgICAgICAgayA9IFZhcjIpICU+JQogIGxlZnRfam9pbih0aXRhbmljX2N2KSAlPiUKICBtdXRhdGUocHJ1bmUgPSBtYXAyKHRyZWUsIGssIH4gcHJ1bmUubWlzY2xhc3MoLngsIGJlc3QgPSAueSkpLAogICAgICAgICBtc2UgPSBtYXAyX2RibChwcnVuZSwgdGVzdCwgZXJyLnJhdGUudHJlZSkpCgp0aXRhbmljX2N2ICU+JQogIGdyb3VwX2J5KGspICU+JQogIHN1bW1hcml6ZSh0ZXN0X21zZSA9IG1lYW4obXNlKSwKICAgICAgICAgICAgc2QgPSBzZChtc2UsIG5hLnJtID0gVFJVRSkpICU+JQogIGdncGxvdChhZXMoaywgdGVzdF9tc2UpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICJUaXRhbmljIHN1cnZpdmFsIHRyZWUiLAogICAgICAgc3VidGl0bGUgPSAiQWdlICsgR2VuZGVyIiwKICAgICAgIHggPSAiTnVtYmVyIG9mIHRlcm1pbmFsIG5vZGVzIiwKICAgICAgIHkgPSAiVGVzdCBlcnJvciByYXRlIikKYGBgCgpIZXJlIEkgc2VsZWN0IDYgYXMgdGhlIG9wdGltYWwgbnVtYmVyIG9mIG5vZGVzLgoKYGBge3IgdGl0YW5pYy10cmVlLTYsIGRlcGVuZHNvbj0idGl0YW5pYy10cmVlIn0KbW9kIDwtIHBydW5lLnRyZWUodGl0YW5pY190cmVlLCBiZXN0ID0gNikKCnRyZWVfZGF0YSA8LSBkZW5kcm9fZGF0YShtb2QpCmdncGxvdChzZWdtZW50KHRyZWVfZGF0YSkpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMC41KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBsYWJlbCh0cmVlX2RhdGEpLCAKICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwgbGFiZWwgPSBsYWJlbF9mdWxsKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKwogIGdlb21fdGV4dChkYXRhID0gbGVhZl9sYWJlbCh0cmVlX2RhdGEpLCAKICAgICAgICAgICAgYWVzKHggPSB4LCB5ID0geSwgbGFiZWwgPSBsYWJlbCksIHZqdXN0ID0gMC41LCBzaXplID0gMykgKwogIHRoZW1lX2RlbmRybygpICsKICBsYWJzKHRpdGxlID0gIlRpdGFuaWMgc3Vydml2YWwgdHJlZSIsCiAgICAgICBzdWJ0aXRsZSA9ICJBZ2UgKyBHZW5kZXIiKQpgYGAKCk5vdGljZSB0aGF0IHNvbWUgYnJhbmNoZXMgc3BsaXQgYW5kIGxlYWQgdG8gdGhlIHNhbWUgb3V0Y29tZS4gRm9yIGluc3RhbmNlLCB0aGUgYm90dG9tLWxlZnQgYnJhbmNoIGFzc2lnbnMgbWFsZXMgd2l0aCBhbiBhZ2UgbGVzcyB0aGFuIDEzIGJ1dCBib3RoIGdyZWF0ZXIgdGhhbiBhbmQgbGVzcyB0aGFuICQyNC43NSQgdG8gYERpZWRgLiBUaGlzIGlzIGJlY2F1c2Ugc3BsaXR0aW5nIHRoZSBub2RlIGxlYWRzIHRvIGluY3JlYXNlZCAqKm5vZGUgcHVyaXR5Kiogd2hlcmUgd2UgYXJlIGV2ZW4gbW9yZSBjb25maWRlbnQgaW4gb3VyIHByZWRpY3Rpb25zLiBUaGluayBhYm91dCBpdC4gSGVyZSBhcmUgdGhlIG91dGNvbWVzIGluIHRoZSB0cmFpbmluZyBvYnNlcnZhdGlvbnMgZm9yIG1hbGVzIG9sZGVyIHRoYW4gMTMgeWVhcnMgb2xkOgoKYGBge3IgdGl0YW5pYy1tLTEzfQp0aXRhbmljX20xMyA8LSB0aXRhbmljICU+JQogIGZpbHRlcihGZW1hbGUgPT0gIm1hbGUiLCBBZ2UgPj0gMTMpICU+JQogIGNvdW50KFN1cnZpdmVkKQoKa25pdHI6OmthYmxlKHRpdGFuaWNfbTEzLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJNYWxlcyBvbGRlciB0aGFuIG9yIGVxdWFsIHRvIDEzIG9uIHRoZSBUaXRhbmljIiwKICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIk91dGNvbWUiLCAiTnVtYmVyIG9mIHRyYWluaW5nIG9ic2VydmF0aW9ucyIpKQpgYGAKCldlIHdvdWxkIHByZWRpY3QgZm9yIGFsbCBvZiB0aGVzZSBvYnNlcnZhdGlvbnMgdGhhdCB0aGUgaW5kaXZpZHVhbCBkaWVkLCBiZWluZyBpbmNvcnJlY3QgYHIgdGl0YW5pY19tMTMkbltbMl1dYCB0aW1lcy4gV2hhdCBoYXBwZW5zIGlmIHdlIHNwbGl0IHRoaXMgc3Vic2V0IGV2ZW4gZnVydGhlcj8KCmBgYHtyIHRpdGFuaWMtbS0xMy1zcGxpdH0KdGl0YW5pYyAlPiUKICBmaWx0ZXIoRmVtYWxlID09ICJtYWxlIiwgQWdlID49IDEzKSAlPiUKICBtdXRhdGUoYWdlMjUgPSBBZ2UgPCAyNC43NSkgJT4lCiAgY291bnQoYWdlMjUsIFN1cnZpdmVkKSAlPiUKICBjb21wbGV0ZShhZ2UyNSwgU3Vydml2ZWQsIGZpbGwgPSBsaXN0KG4gPSAwKSkgJT4lCiAga25pdHI6OmthYmxlKGNvbC5uYW1lcyA9IGMoIkxlc3MgdGhhbiAyNC43NSB5ZWFycyBvbGQiLCAiT3V0Y29tZSIsICJOdW1iZXIgb2YgdHJhaW5pbmcgb2JzZXJ2YXRpb25zIikpCmBgYAoKRm9yIG1hbGVzIGJldHdlZW4gMTMgYW5kIDI0Ljc1IHllYXJzIG9sZCwgdGhlIGRlY2lzaW9uIHRyZWUgcHJlZGljdGlvbiBhY2hpZXZlcyBoaWdoZXIgbm9kZSBwdXJpdHkgLSBpdCBtb3JlIGFjY3VyYXRlbHkgcHJlZGljdHMgYWxsIG9mIHRoZSB0cmFpbmluZyBvYnNlcnZhdGlvbnMgYXMgZGVhZC4gSWYgd2UgaGFkIGEgdGVzdCBvYnNlcnZhdGlvbiBmb3IgYSAxNi15ZWFyLW9sZCBtYWxlLCB3ZSdkIGJlIG1vcmUgY29uZmlkZW50IGluIG91ciBwcmVkaWN0aW9uIHRoYW4gaWYgd2UgaGFkIHRlcm1pbmF0ZWQgdGhlIG5vZGUgYXQgJFx0ZXh0e2FnZX0gPCAxMyQuIFdoaWxlIHRoaXMgZG9lcyBub3QgaW1wcm92ZSBvdXIgZXJyb3IgcmF0ZSAod2Ugd291bGQgaGF2ZSBtYWRlIHRoZSBzYW1lIHByZWRpY3Rpb24gcmVnYXJkbGVzcyksIGl0IGRvZXMgaW1wcm92ZSBvdXIgR2luaSBpbmRleCBhbmQgY3Jvc3MtZW50cm9weSB3aGljaCBhcmUgdGhlIG1lYXN1cmVzIHVzZWQgdG8gZ3JvdyB0aGUgdHJlZS4KCiMjIFRyZWVzIHZzLiByZWdyZXNzaW9uCgpMaW5lYXIgcmVncmVzc2lvbiBhbmQgZGVjaXNpb24gdHJlZXMgdXRpbGl6ZSBlbnRpcmVseSBkaWZmZXJlbnQgZnVuY3Rpb25hbCBmb3Jtcy4gTGluZWFyIHJlZ3Jlc3Npb24gYXNzdW1lcyBsaW5lYXIgYW5kIGFkZGl0aXZlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBwcmVkaWN0b3JzIGFuZCB0aGUgcmVzcG9uc2U6CgokJGYoWCkgPSBcYmV0YV8wICsgXHN1bV97aiA9IDF9XnAgWF9qIFxiZXRhX2okJAoKV2hlcmVhcyBkZWNpc2lvbiB0cmVlcyBhc3N1bWUgdGhlIG9ic2VydmF0aW9ucyBjYW4gYmUgcGFydGl0aW9uZWQgaW50byB0aGUgZmVhdHVyZSBzcGFjZToKCiQkZihYKSA9IFxzdW1fe20gPSAxfV5NIGNfbSBcY2RvdCAxX3tYIFxpbiBSX219JCQKCklmIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcHJlZGljdG9yKHMpIGFuZCB0aGUgcmVzcG9uc2UgYXJlIHRydWx5IGxpbmVhciBhbmQgYWRkaXRpdmUsIHRoZW4gbGluZWFyIHJlZ3Jlc3Npb24gd2lsbCBsaWtlbHkgcGVyZm9ybSBiZXR0ZXIgdGhhbiBhIGRlY2lzaW9uIHRyZWUuIElmIHRoZSByZWxhdGlvbnNoaXAgaXMgaGlnaGx5IGNvbXBsZXggYW5kIG5vbi1saW5lYXIsIHRoZW4gZGVjaXNpb24gdHJlZXMgbWF5IGJlIHRoZSBiZXR0ZXIgb3B0aW9uLiBVc2luZyByZXNhbXBsaW5nIG1ldGhvZHMgc3VjaCBhcyBjcm9zcy12YWxpZGF0aW9uIGNhbiBoZWxwIHlvdSB0byBkZWNpZGUgdGhlIGFwcHJvcHJpYXRlIHN0YXRpc3RpY2FsIGxlYXJuaW5nIG1ldGhvZC4KCiMjIEJlbmVmaXRzL2RyYXdiYWNrcyB0byBkZWNpc2lvbiB0cmVlcwoKRGVjaXNpb24gdHJlZXMgYXJlIGFuIGVudGlyZWx5IGRpZmZlcmVudCBtZXRob2Qgb2YgZXN0aW1hdGluZyBmdW5jdGlvbmFsIGZvcm1zIGFzIGNvbXBhcmVkIHRvIGxpbmVhciByZWdyZXNzaW9uLiBUaGVyZSBhcmUgc29tZSBiZW5lZml0cyB0byB0cmVlczoKCiogVGhleSBhcmUgZWFzeSB0byBleHBsYWluLiBNb3N0IHBlb3BsZSwgZXZlbiBpZiB0aGV5IGxhY2sgc3RhdGlzdGljYWwgdHJhaW5pbmcsIGNhbiB1bmRlcnN0YW5kIGRlY2lzaW9uIHRyZWVzLgoqIFRoZXkgYXJlIGVhc2lseSBwcmVzZW50ZWQgYXMgdmlzdWFsaXphdGlvbnMsIGFuZCBwcmV0dHkgaW50ZXJwcmV0YWJsZS4KKiBRdWFsaXRhdGl2ZSBwcmVkaWN0b3JzIGFyZSBlYXNpbHkgaGFuZGxlZCB3aXRob3V0IHRoZSBuZWVkIHRvIGNyZWF0ZSBhIGxvbmcgc2VyaWVzIG9mIGR1bW15IHZhcmlhYmxlcy4KCkhvd2V2ZXIgdGhlcmUgYXJlIGFsc28gZHJhd2JhY2tzIHRvIHRyZWVzOgoKKiBUaGVpciBhY2N1cmFjeSByYXRlcyBhcmUgZ2VuZXJhbGx5IGxvd2VyIHRoYW4gb3RoZXIgcmVncmVzc2lvbiBhbmQgY2xhc3NpZmljYXRpb24gYXBwcm9hY2hlcy4KKiBUcmVlcyBjYW4gYmUgbm9uLXJvYnVzdC4gVGhhdCBpcywgYSBzbWFsbCBjaGFuZ2UgaW4gdGhlIGRhdGEgb3IgaW5jbHVzaW9uL2V4Y2x1c2lvbiBvZiBhIGhhbmRmdWwgb2Ygb2JzZXJ2YXRpb25zIGNhbiBkcmFtYXRpY2FsbHkgYWx0ZXIgdGhlIGZpbmFsIGVzdGltYXRlZCB0cmVlLiBGb3IgZXhhbXBsZSwgbGV0J3MgZXN0aW1hdGUgYSBkZWNpc2lvbiB0cmVlIGZvciB0aGUgaGlnaHdheSBtaWxlYWdlIGV4YW1wbGUgKCROID0gYHIgbnJvdyhBdXRvKWAkKSBieSBzcGxpdHRpbmcgdGhlIGRhdGEgaW50byBhIHRyYWluaW5nL3Rlc3Qgc2V0ICg3MC8zMCUpIGFuZCBlc3RpbWF0aW5nIHRoZSB0ZXN0IE1TRSwgYW5kIHJlcGVhdCB0aGlzIHByb2Nlc3MgMTAwMCB0aW1lcyB1c2luZyByYW5kb20gY29tYmluYXRpb25zIG9mIHRyYWluaW5nL3Rlc3Qgc2V0czoKCiAgICBgYGB7ciBhdXRvLXRyZWUtdmFsfQogICAgYXV0b192YWxfdGVzdCA8LSBmdW5jdGlvbigpewogICAgICAjIHNwbGl0IGRhdGEKICAgICAgYXV0b19zcGxpdCA8LSByZXNhbXBsZV9wYXJ0aXRpb24oQXV0bywgcCA9IGModGVzdCA9IDAuMywgdHJhaW4gPSAwLjcpKQogICAgICAKICAgICAgIyBlc3RpbWF0ZSBtb2RlbAogICAgICB2YWwgPC0gdHJlZShtcGcgfiBob3JzZXBvd2VyICsgd2VpZ2h0LCBkYXRhID0gYXV0b19zcGxpdCR0cmFpbikKICAgICAgCiAgICAgICMgZXN0aW1hdGUgdGVzdCBtc2UKICAgICAgbXNlKHZhbCwgYXV0b19zcGxpdCR0ZXN0KQogICAgfQogICAgCiAgICAjIHJlcGVhdCB0aGUgcHJvY2VkdXJlIDEwMCB0aW1lcwogICAgdmFsX21zZSA8LSBkYXRhX2ZyYW1lKGlkID0gMToxMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1zZSA9IG1hcF9kYmwoaWQsIH4gYXV0b192YWxfdGVzdCgpKSkKICAgIAogICAgIyBkaXN0cmlidXRpb24gb2YgdGhlIG1zZQogICAgZ2dwbG90KHZhbF9tc2UsIGFlcyhtc2UpKSArCiAgICAgIGdlb21faGlzdG9ncmFtKCkgKwogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKHZhbF9tc2UkbXNlKSkKICAgIGBgYAoKVGhlIGRpc3RyaWJ1dGlvbiBvZiB0ZXN0IE1TRXMgaXMgcXVpdGUgbGFyZ2UgZm9yIGVhY2ggb2YgdGhlIHNwbGl0cywgaW5kaWNhdGluZyBzdWJzdGFudGlhbCB2YXJpYW5jZSBpbiBvdXIgZXN0aW1hdGUgb2YgdGhlIHRlc3QgTVNFLiBGb3J0dW5hdGVseSwgdGhlcmUgaXMgYW4gZWFzeSB3YXkgdG8gaW1wcm92ZSBvbiB0aGVzZSBwb29yIHByZWRpY3Rpb25zOiBieSBhZ2dyZWdhdGluZyBtYW55IGRlY2lzaW9uIHRyZWVzIGFuZCBhdmVyYWdpbmcgYWNyb3NzIHRoZW0sIHdlIGNhbiBzdWJzdGFudGlhbGx5IGltcHJvdmUgcGVyZm9ybWFuY2UuCgojIEJhZ2dpbmcKCkRlY2lzaW9uIHRyZWVzIHN1ZmZlciBmcm9tICoqaGlnaCB2YXJpYW5jZSoqOiBhcyB3ZSBzYXcgYWJvdmUsIGV2ZW4gYSBzbWFsbCBjaGFuZ2UgaW4gdGhlIHRyYWluaW5nL3Rlc3Qgc2V0IHBhcnRpdGlvbnMgY2FuIGxlYWQgdG8gc3Vic3RhbnRpYWwgY2hhbmdlcyBpbiB0aGUgZXN0aW1hdGVkIG1vZGVsIGFuZCByZXN1bHRpbmcgZml0LiBIb3dldmVyIGEgbWV0aG9kIGltcGxlbWVudGluZyAqKmxvdyB2YXJpYW5jZSoqIHNob3VsZCBwcm92aWRlIG1vcmUgY29uc2lzdGVudCBlc3RpbWF0ZXMsIHJlZ2FyZGxlc3Mgb2YgdGhlIHNhbXBsZSBzcGxpdC4gQnkgKipib290c3RyYXAgYWdncmVnYXRpbmcqKiwgb3Igc2ltcGx5ICoqYmFnZ2luZyoqLCBpcyBhIGdlbmVyYWwgbWV0aG9kIGZvciByZWR1Y2luZyB2YXJpYW5jZSBpbiBlc3RpbWF0ZXMuCgpXZSBhbHJlYWR5IG1ldCB0aGUgW2Jvb3RzdHJhcF0ocGVyc3AwMDZfcmVzYW1wbGluZy5odG1sI3RoZV9ib290c3RyYXApLiBSZWNhbGwgdGhhdCB0aGlzIGludm9sdmVzIHJlcGVhdGVkbHkgc2FtcGxpbmcgd2l0aCByZXBsYWNlbWVudCBmcm9tIGEgc2FtcGxlLCBlc3RpbWF0aW5nIGEgcGFyYW1ldGVyIG9yIHNldCBvZiBwYXJhbWV0ZXJzIGZvciBlYWNoIGJvb3RzdHJhcCBzYW1wbGUsIHRoZW4gYXZlcmFnaW5nIGFjcm9zcyB0aGUgYm9vdHN0cmFwIHNhbXBsZXMgdG8gZm9ybSBvdXIgYm9vdHN0cmFwIGVzdGltYXRlIG9mIHRoZSBwYXJhbWV0ZXIuIEJ5IGF2ZXJhZ2luZyBhY3Jvc3MgYWxsIHRoZSBib290c3RyYXAgc2FtcGxlcywgd2UgcmVkdWNlIHRoZSB2YXJpYW5jZSAkXHNpZ21hXjIkIGluIG91ciBmaW5hbCBlc3RpbWF0ZS5eW1RoZSB2YXJpYW5jZSBmb3IgZWFjaCBvYnNlcnZhdGlvbiBpbiBhbiBpbmRlcGVuZGVudCBzYW1wbGUgJFpfMSwgWl8yLCBcZG90cywgWl9uJCBpcyAkXHNpZ21hXjIkLiBUaGUgdmFyaWFuY2UgZm9yIHRoZSBhdmVyYWdlIG9mIHRoZSBzYW1wbGUgJFxiYXJ7Wn0kIGlzICRcZnJhY3tcc2lnbWFeMn17bn0kLiBCeSBhdmVyYWdpbmcgYWNyb3NzIHRoZSBvYnNlcnZhdGlvbnMsIHdlIHJlZHVjZSB0aGUgZXN0aW1hdGVkIHZhcmlhbmNlLiBJbnR1aXRpdmVseSB0aGlzIG1ha2VzIHNlbnNlIGJlY2F1c2Ugb3VyIGVzdGltYXRlIG9mICRcYmFye1p9JCBpcyBiYXNlZCBvbiBtb3JlIGluZm9ybWF0aW9uLCBhbmQgc2hvdWxkIHRoZXJlZm9yZSBiZSBtb3JlIHN0YWJsZS5dCgpBcyB0aGlzIGFwcGxpZXMgdG8gc3RhdGlzdGljYWwgbGVhcm5pbmcgbWV0aG9kcywgd2UgZXN0aW1hdGUgJFxoYXR7Zn1eMSh4KSwgXGhhdHtmfV4yKHgpLCBcZG90cywgXGhhdHtmfV5CKHgpJCB1c2luZyAkQiQgc2VwYXJhdGUgdHJhaW5pbmcgc2V0cywgYW5kIGF2ZXJhZ2UgYWNyb3NzIHRoZSBtb2RlbHMgdG8gZ2VuZXJhdGUgYSBzaW5nbGUgbG93LXZhcmlhbmNlIG1vZGVsOgoKJCRcaGF0e2Z9X3tcdGV4dHthdmd9fSh4KSA9IFxmcmFjezF9e0J9IFxzdW1fe2IgPSAxfV5CIFxoYXR7Zn1eYih4KSQkCgpTaW5jZSB3ZSBkb24ndCBoYXZlIHRoYXQgbWFueSB0cmFpbmluZyBzZXRzLCB3ZSBib290c3RyYXAgdGhlbSwganVzdCBsaWtlIGhvdyB3ZSBbZXN0aW1hdGVkIGJvb3RzdHJhcCBwYXJhbWV0ZXJzIGZvciBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsXShwZXJzcDAwNl9yZXNhbXBsaW5nLmh0bWwjZXN0aW1hdGluZ190aGVfYWNjdXJhY3lfb2ZfYV9saW5lYXJfcmVncmVzc2lvbl9tb2RlbCkuIFdlIGVzdGltYXRlIGEgZGVjaXNpb24gdHJlZSBtb2RlbCBvbiBlYWNoIGJvb3RzdHJhcCBzYW1wbGUgYW5kIGF2ZXJhZ2UgdGhlIHJlc3VsdHMgb2YgdGhlIG1vZGVscyB0byBnZW5lcmF0ZSB0aGUgYmFnZ2VkIGVzdGltYXRlOgoKJCRcaGF0e2Z9X3tcdGV4dHtiYWd9fSh4KSA9IFxmcmFjezF9e0J9IFxzdW1fe2IgPSAxfV5CIFxoYXR7Zn1eYih4KSQkCgpFYWNoIHRyZWUgaXMgZ3Jvd24gd2l0aG91dCBwcnVuaW5nLCBzbyB0aGV5IGFyZSBoaWdoLXZhcmlhbmNlIGJ1dCBsb3ctYmlhcy4gVGhlbiBieSBhdmVyYWdpbmcgYWNyb3NzIHRoZSByZXN1bHRzLCB3ZSBzaG91bGQgZ2V0IGFuIGVzdGltYXRlIHRoYXQgaGFzIGxvdy1iaWFzICoqYW5kKiogbG93LXZhcmlhbmNlLiBGb3IgcmVncmVzc2lvbiB0cmVlcyB0aGlzIGlzIHN0cmFpZ2h0LWZvcndhcmQuIEZvciBjbGFzc2lmaWNhdGlvbiB0cmVlcywgd2UgZXN0aW1hdGUgJEIkIHRyZWVzIGFuZCBmb3IgYSBnaXZlbiB0ZXN0IG9ic2VydmF0aW9uIGFzc2lnbiBpdCB0aGUgKiptYWpvcml0eS1jbGFzcyByZXN1bHQqKjogdGhlIG92ZXJhbGwgcHJlZGljdGlvbiBpcyB0aGUgbW9zdCBjb21tb25seSBvY2N1cnJpbmcgcHJlZGljdGVkIG91dGNvbWUgYWNyb3NzIGFsbCB0aGUgJEIkIHByZWRpY3Rpb25zLiBDb21wYXJlZCB0byB0aGUgZXJyb3IgcmF0ZSBmb3IgdGhlIGNvcnJlc3BvbmRpbmcgY2xhc3NpZmljYXRpb24gdHJlZSwgYmFnZ2VkIGVzdGltYXRlcyBnZW5lcmFsbHkgaGF2ZSBzbGlnaHRseSBsb3dlciBlcnJvciByYXRlcy4KCiMjIE91dC1vZi1iYWcgZXN0aW1hdGVzCgpGb3J0dW5hdGVseSB1c2luZyBhIGJhZ2dlZCBhcHByb2FjaCBhbHNvIGFsbG93cyB1cyB0byBhdm9pZCB1c2luZyBhbnkgdHlwZSBvZiByZXNhbXBsaW5nIG1ldGhvZCB0byBjYWxjdWxhdGUgdGhlIHRlc3QgTVNFIG9yIGVycm9yIHJhdGUuIFRoaXMgaXMgYmVjYXVzZSB3ZSBoYXZlIGEgbmF0dXJhbCB0ZXN0IHNldCBhcyBhIHJlc3VsdCBvZiB0aGUgYm9vdHN0cmFwcGluZyBwcm9jZXNzLiBSZWNhbGwgdGhhdCBpbiBhIGJvb3RzdHJhcCBzYW1wbGluZyBwcm9jZXNzLCB3ZSAqKnNhbXBsZSB3aXRoIHJlcGxhY2VtZW50KiouIFRoaXMgbWVhbnMgdGhhdCBpbiBzb21lIGJvb3RzdHJhcCBzYW1wbGVzLCBhbiBvYnNlcnZhdGlvbiBtYXkgbmV2ZXIgYmUgZHJhd24uIEluIGZhY3QsIHRoZXJlIGlzIGEgcGF0dGVybiB0byB0aGlzIHBoZW5vbWVub24uIE9uIGF2ZXJhZ2UsIGVhY2ggYmFnZ2VkIHRyZWUgdXNlcyBhcHByb3hpbWF0ZWx5IHR3by10aGlyZHMgb2YgdGhlIG9yaWdpbmFsIG9ic2VydmF0aW9ucy4gVGhlcmVmb3JlIG9ic2VydmF0aW9ucyBub3QgYXBwZWFyaW5nIGluIGEgZ2l2ZW4gYmFnIGFyZSBjb25zaWRlcmVkICoqb3V0LW9mLWJhZyBvYnNlcnZhdGlvbnMqKiAoT09CKS4KCmBgYHtyIGJvb3QtcHJvcH0KIyBnZW5lcmF0ZSBzYW1wbGUgaW5kZXgKc2FtcCA8LSBkYXRhX2ZyYW1lKHggPSBzZXEuaW50KDEwMDApKQoKIyBnZW5lcmF0ZSBib290c3RyYXAgc2FtcGxlIGFuZCBjb3VudCBwcm9wb3J0aW9uIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGRyYXcKcHJvcF9kcmF3biA8LSBib290c3RyYXAoc2FtcCwgbiA9IG5yb3coc2FtcCkpICU+JQogIG11dGF0ZShzdHJhcCA9IG1hcChzdHJhcCwgYXNfdGliYmxlKSkgJT4lCiAgdW5uZXN0KHN0cmFwKSAlPiUKICBtdXRhdGUoZHJhd24gPSBUUlVFKSAlPiUKICBjb21wbGV0ZSguaWQsIHgsIGZpbGwgPSBsaXN0KGRyYXduID0gRkFMU0UpKSAlPiUKICBkaXN0aW5jdCAlPiUKICBncm91cF9ieSh4KSAlPiUKICBtdXRhdGUobl9kcmF3biA9IGN1bXN1bShkcmF3biksCiAgICAgICAgIC5pZCA9IGFzLm51bWVyaWMoLmlkKSwKICAgICAgICAgbl9wcm9wID0gbl9kcmF3biAvIC5pZCkKCmdncGxvdChwcm9wX2RyYXduLCBhZXMoLmlkLCBuX3Byb3AsIGdyb3VwID0geCkpICsKICBnZW9tX2xpbmUoYWxwaGEgPSAuMDUpICsKICBsYWJzKHggPSAiYi10aCBib290c3RyYXAgc2FtcGxlICIsCiAgICAgICB5ID0gIlByb3BvcnRpb24gaS10aCBvYnNlcnZhdGlvbiBpbiBzYW1wbGVzIDE6YiIpCmBgYAoKQmVjYXVzZSBvZiB0aGlzLCB3ZSBjYW4gY2FsY3VsYXRlIHRoZSAqKm91dC1vZi1iYWcgZXJyb3IgZXN0aW1hdGUqKiwgb3IgdGhlIGF2ZXJhZ2UgZXJyb3IgZXN0aW1hdGUgZm9yIG91dC1vZi1iYWcgb2JzZXJ2YXRpb25zLiBGaXJzdCB3ZSBnZW5lcmF0ZSBiYWdnZWQgcHJlZGljdGlvbnMgZm9yIGVhY2ggb2JzZXJ2YXRpb24gJGkkIHVzaW5nIG9ubHkgaXRzIE9PQiBlc3RpbWF0ZXMsIHRoZW4gd2UgYXZlcmFnZSBhY3Jvc3MgYWxsICRpJCBvYnNlcnZhdGlvbnMgdG8gZ2V0IHRoZSBPT0IgZXJyb3IgZXN0aW1hdGUuIFRoaXMgaXMgYSB2YWxpZCBlc3RpbWF0ZSBvZiB0aGUgdGVzdCBlcnJvciByYXRlL01TRSBiZWNhdXNlIGl0IG9ubHkgdXNlcyBvYnNlcnZhdGlvbnMgdGhhdCB3ZXJlIG5vdCBwYXJ0IG9mIHRoZSB0cmFpbmluZyBvYnNlcnZhdGlvbnMgZm9yIGEgZ2l2ZW4gYmFnICRiJC4gVGhpcyBpcyBmYXIgbW9yZSBjb21wdXRhdGlvbmFsbHkgYWR2YW50YWdlb3VzIHRoYW4gY2FsY3VsYXRpbmcgYSBjcm9zcy12YWxpZGF0ZWQgZXJyb3IgcmF0ZSBmb3IgYSBiYWdnZWQgbW9kZWwuIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSBwcmVkaWN0aW5nIHN1cnZpdmFsIG9uIHRoZSBUaXRhbmljIHVzaW5nIGFsbCBhdmFpbGFibGUgcHJlZGljdG9ycyBpbiB0aGUgZGF0YXNldDpeW1RoaXMgaW5jbHVkZXMgYWxsIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBmcmFtZSB0aGF0IGFyZSBub3QgbWVyZWx5IHRleHQgdmFsdWVzLl0KCmBgYHtyIHRpdGFuaWMtYmFnLW9vYn0KdGl0YW5pY19yZl9kYXRhIDwtIHRpdGFuaWMgJT4lCiAgICBzZWxlY3QoLU5hbWUsIC1UaWNrZXQsIC1DYWJpbiwgLVNleCwgLVBhc3NlbmdlcklkKSAlPiUKICAgIG11dGF0ZV9lYWNoKGZ1bnMoYXMuZmFjdG9yKC4pKSwgUGNsYXNzLCBFbWJhcmtlZCkgJT4lCiAgICBuYS5vbWl0CgoodGl0YW5pY19iYWcgPC0gcmFuZG9tRm9yZXN0KFN1cnZpdmVkIH4gLiwgZGF0YSA9IHRpdGFuaWNfcmZfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gNywgbnRyZWUgPSA1MDApKQpgYGAKCiMjIyMjIEVzdGltYXRpb24gdGltZSBmb3IgT09CIGVycm9yIHJhdGUKCmBgYHtyIHRpdGFuaWMtYmFnLW9vYi10aW1lfQpzeXN0ZW0udGltZSh7CiAgcmFuZG9tRm9yZXN0KFN1cnZpdmVkIH4gLiwgZGF0YSA9IHRpdGFuaWNfcmZfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDcsIG50cmVlID0gNTAwKQp9KQpgYGAKCiMjIyMjIEVzdGltYXRpb24gdGltZSBmb3IgJDEwJC1mb2xkIENWIGVycm9yIHJhdGUKCmBgYHtyIHRpdGFuaWMtYmFnLWN2LXRpbWV9CnN5c3RlbS50aW1lKHsKICBjcm9zc3Zfa2ZvbGQodGl0YW5pY19yZl9kYXRhLCBrID0gMTApICU+JQogICAgbXV0YXRlKG1vZGVsID0gbWFwKHRyYWluLCB+IHJhbmRvbUZvcmVzdChTdXJ2aXZlZCB+IC4sIGRhdGEgPSAuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gNywgbnRyZWUgPSA1MDApKSwKICAgICAgICAgICB0ZXN0LmVyciA9IG1hcDJfZGJsKG1vZGVsLCB0ZXN0LCBlcnIucmF0ZS5yZikpICU+JQogICAgc3VtbWFyaXplKG1lYW4odGVzdC5lcnIpKQp9KQpgYGAKCkZvciBvdXIgVGl0YW5pYyBiYWdnZWQgbW9kZWwgd2l0aCBhbGwgYXZhaWxhYmxlIHByZWRpY3RvcnMsIHdlIGVzdGltYXRlIGFuIE9PQiBlcnJvciByYXRlIG9mICRgciB0YWlsKHRpdGFuaWNfYmFnJGVyci5yYXRlWywxXSwgbiA9IDEpICogMTAwYFwlJC4gTGlrZXdpc2UsIHdlIG9idGFpbiBhIFtjb25mdXNpb24gbWF0cml4XShwZXJzcDAwNF9sb2dpc3RpY19yZWdyZXNzaW9uLmh0bWwjY29uZnVzaW9uX21hdHJpeCkgdG8gaWRlbnRpZnkgb3VyIGVycm9yIHJhdGUgZm9yIGVhY2ggY2xhc3MuCgojIyBWYXJpYWJsZSBpbXBvcnRhbmNlIG1lYXN1cmVzCgpJbnRlcnByZXRpbmcgYSBiYWdnZWQgbW9kZWwgaXMgbXVjaCBtb3JlIGRpZmZpY3VsdCB0aGFuIGludGVycHJldGluZyBhIHNpbmdsZSBkZWNpc2lvbiB0cmVlLiBCZWNhdXNlIGVhY2ggdHJlZSBpcyB1bmlxdWUsIHdlIGNhbm5vdCBwbG90IGFuICJhdmVyYWdlIiBvZiB0aGUgdHJlZXMgbGlrZSB3ZSBtaWdodCB3aXRoIGEgYm9vdHN0cmFwcGVkIGxpbmVhciBtb2RlbC4gVGhlIG1vc3QgY29tbW9uIG1ldGhvZCBvZiBpbnRlcnByZXRhdGlvbiAoYmV5b25kIHByZWRpY3Rpb24gYWNjdXJhY3kpIGlzICoqdmFyaWFibGUgaW1wb3J0YW5jZSoqLCBvciBhdHRlbXB0aW5nIHRvIGFzc2VzcyBob3cgaW1wb3J0YW50IGVhY2ggdmFyaWFibGUgaXMgdG8gdGhlIG1vZGVsLiBJbiByZWdyZXNzaW9uIHRyZWVzLCBmb3IgZWFjaCBwcmVkaWN0b3Igd2UgY2FsY3VsYXRlIHRoZSB0b3RhbCBhbW91bnQgb2YgcmVkdWN0aW9uIGluIHRoZSBSU1MgYXR0cmlidXRhYmxlIHRvIHNwbGl0cyBjYXVzZWQgYnkgdGhlIHByZWRpY3RvciwgYXZlcmFnZWQgb3ZlciB0aGUgJEIkIHRyZWVzLiBGb3IgY2xhc3NpZmljYXRpb24gdHJlZXMsIHdlIGRvIHRoZSBzYW1lIHRoaW5nIHVzaW5nIGF2ZXJhZ2UgcmVkdWN0aW9uIGluIHRoZSBHaW5pIGluZGV4LgoKYGBge3IgdGl0YW5pYy12YXJpbXB9CmRhdGFfZnJhbWUodmFyID0gcm93bmFtZXMoaW1wb3J0YW5jZSh0aXRhbmljX2JhZykpLAogICAgICAgICAgIE1lYW5EZWNyZWFzZUdpbmkgPSBpbXBvcnRhbmNlKHRpdGFuaWNfYmFnKVssMV0pICU+JQogIG11dGF0ZSh2YXIgPSBmY3RfcmVvcmRlcih2YXIsIE1lYW5EZWNyZWFzZUdpbmksIGZ1biA9IG1lZGlhbikpICU+JQogIGdncGxvdChhZXModmFyLCBNZWFuRGVjcmVhc2VHaW5pKSkgKwogIGdlb21fcG9pbnQoKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHRpdGxlID0gIlByZWRpY3Rpbmcgc3Vydml2YWwgb24gdGhlIFRpdGFuaWMiLAogICAgICAgc3VidGl0bGUgPSAiQmFnZ2luZyIsCiAgICAgICB4ID0gTlVMTCwKICAgICAgIHkgPSAiQXZlcmFnZSBkZWNyZWFzZSBpbiB0aGUgR2luaSBJbmRleCIpCmBgYAoKRm9yIGNsYXNzaWZpY2F0aW9uIHRyZWVzLCBsYXJnZXIgdmFsdWVzIGFyZSBiZXR0ZXIuIFNvIGZvciB0aGUgVGl0YW5pYyBiYWdnZWQgbW9kZWwsIGdlbmRlciwgYWdlLCBhbmQgZmFyZSBhcmUgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMsIHdoZXJlYXMgbnVtYmVyIG9mIHNpYmxpbmdzL3BhcmVudHMgYWJvYXJkIGFuZCB0aGUgcG9ydCBvZiBkZXBhcnR1cmUgYXJlIHJlbGF0aXZlbHkgdW5pbXBvcnRhbnQuCgojIFJhbmRvbSBmb3Jlc3RzCgoqKlJhbmRvbSBmb3Jlc3RzKiogaW1wcm92ZSB1cG9uIGJhZ2dpbmcgYnkgZGVjb3JyZWxhdGluZyB0aGUgaW5kaXZpZHVhbCB0cmVlcy4gVGhlIHByb2JsZW0gd2l0aCBiYWdnaW5nIGlzIHRoYXQgaWYgdGhlcmUgaXMgYSBzaW5nbGUgZG9taW5hbnQgcHJlZGljdG9yIGluIHRoZSBkYXRhc2V0LCBtb3N0IHRyZWVzIHdpbGwgdXNlIHRoZSBzYW1lIHByZWRpY3RvciBmb3IgdGhlIGZpcnN0IHNwbGl0IGFuZCBlbnN1cmUgY29ycmVsYXRpb24gYW5kIHNpbWlsYXJpdHkgYW1vbmcgdGhlIHRyZWVzLiBSZW1lbWJlciB0aGF0IHRoZSBnb2FsIG9mIGJhZ2dpbmcgaXMgdG8gcmVkdWNlIHRoZSB2YXJpYW5jZSBvZiBvdXIgZXN0aW1hdGVzIG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZSAkWSQuIEJ1dCBhdmVyYWdpbmcgYWNyb3NzIGEgc2V0IG9mIGNvcnJlbGF0ZWQgdHJlZXMgd2lsbCBub3Qgc3Vic3RhbnRpYWxseSByZWR1Y2UgdmFyaWFuY2UsIGF0IGxlYXN0IG5vdCBhcyBtdWNoIGFzIGlmIHRoZSB0cmVlcyB3ZXJlIHVuY29ycmVsYXRlZC4KClRvIHJlc29sdmUgdGhpcyBwcm9ibGVtLCB3aGVuIHNwbGl0dGluZyBhIHRyZWUgcmFuZG9tIGZvcmVzdHMgd2lsbCBvbmx5IGNvbnNpZGVyIGEgcmFuZG9tIHNhbXBsZSAkbSQgb2YgdGhlIHRvdGFsIHBvc3NpYmxlIHByZWRpY3RvcnMgJHAkLiBUaGF0IGlzLCBpdCBpbnRlbnRpb25hbGx5IGlnbm9yZXMgYSByYW5kb20gc2V0IG9mIHZhcmlhYmxlcy4gRXZlcnkgdGltZSBhIG5ldyBzcGxpdCBpcyBjb25zaWRlcmVkLCBhIG5ldyByYW5kb20gc2FtcGxlICRtJCBpcyBkcmF3bi4gVGhlIG1haW4gcXVlc3Rpb24gdGhlbiBiZWNvbWVzIGhvdyB0byBzZWxlY3QgdGhlIHNpemUgb2YgJG0kLiBJU0wgcmVjb21tZW5kcyAkbSA9IFxzcXJ0e3B9JC4gQnkgZGVmYXVsdCwgdGhlIGByYW5kb21Gb3Jlc3RgIHBhY2thZ2UgdXNlcyAkbSA9IFxzcXJ0e3B9JCBmb3IgY2xhc3NpZmljYXRpb24gdHJlZXMgYW5kICRtID0gXGZyYWN7cH17M30kIGZvciByZWdyZXNzaW9uIHRyZWVzLgoKTGV0J3MgY29tcGFyZSB0aGUgcmVzdWx0cyBvZiB0aGUgYmFnZ2VkIFRpdGFuaWMgbW9kZWwgdG8gdGhlIHNhbWUgbW9kZWwsIG9ubHkgdGhpcyB0aW1lIGVtcGxveWluZyB0aGUgcmFuZG9tIGZvcmVzdCBtZXRob2Q6CgojIyMjIyBCYWdnZWQgbW9kZWwKCmBgYHtyIHRpdGFuaWMtYmFnLXJlcHJpbnR9CnRpdGFuaWNfYmFnCmBgYAoKYGBge3IgdGl0YW5pYy1iYWctZmlyc3Qtc3BsaXR9CnNlcS5pbnQodGl0YW5pY19iYWckbnRyZWUpICU+JQogIG1hcF9kZih+IGdldFRyZWUodGl0YW5pY19iYWcsIGsgPSAuLCBsYWJlbFZhciA9IFRSVUUpWzEsXSkgJT4lCiAgY291bnQoYHNwbGl0IHZhcmApICU+JQogIGtuaXRyOjprYWJsZShjYXB0aW9uID0gIlZhcmlhYmxlIHVzZWQgdG8gZ2VuZXJhdGUgdGhlIGZpcnN0IHNwbGl0IGluIGVhY2ggdHJlZSIsCiAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlIHVzZWQgdG8gc3BsaXQiLCAiTnVtYmVyIG9mIHRyYWluaW5nIG9ic2VydmF0aW9ucyIpKQpgYGAKCiMjIyMjIFJhbmRvbSBmb3Jlc3QgbW9kZWwKCmBgYHtyIHRpdGFuaWMtcmZ9Cih0aXRhbmljX3JmIDwtIHJhbmRvbUZvcmVzdChTdXJ2aXZlZCB+IC4sIGRhdGEgPSB0aXRhbmljX3JmX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDUwMCkpCgpzZXEuaW50KHRpdGFuaWNfcmYkbnRyZWUpICU+JQogIG1hcF9kZih+IGdldFRyZWUodGl0YW5pY19yZiwgayA9IC4sIGxhYmVsVmFyID0gVFJVRSlbMSxdKSAlPiUKICBjb3VudChgc3BsaXQgdmFyYCkgJT4lCiAga25pdHI6OmthYmxlKGNhcHRpb24gPSAiVmFyaWFibGUgdXNlZCB0byBnZW5lcmF0ZSB0aGUgZmlyc3Qgc3BsaXQgaW4gZWFjaCB0cmVlIiwKICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygiVmFyaWFibGUgdXNlZCB0byBzcGxpdCIsICJOdW1iZXIgb2YgdHJhaW5pbmcgb2JzZXJ2YXRpb25zIikpCgpkYXRhX2ZyYW1lKHZhciA9IHJvd25hbWVzKGltcG9ydGFuY2UodGl0YW5pY19yZikpLAogICAgICAgICAgIGBSYW5kb20gZm9yZXN0YCA9IGltcG9ydGFuY2UodGl0YW5pY19yZilbLDFdKSAlPiUKICBsZWZ0X2pvaW4oZGF0YV9mcmFtZSh2YXIgPSByb3duYW1lcyhpbXBvcnRhbmNlKHRpdGFuaWNfcmYpKSwKICAgICAgICAgICBCYWdnaW5nID0gaW1wb3J0YW5jZSh0aXRhbmljX2JhZylbLDFdKSkgJT4lCiAgbXV0YXRlKHZhciA9IGZjdF9yZW9yZGVyKHZhciwgQmFnZ2luZywgZnVuID0gbWVkaWFuKSkgJT4lCiAgZ2F0aGVyKG1vZGVsLCBnaW5pLCAtdmFyKSAlPiUKICBnZ3Bsb3QoYWVzKHZhciwgZ2luaSwgY29sb3IgPSBtb2RlbCkpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh0aXRsZSA9ICJQcmVkaWN0aW5nIHN1cnZpdmFsIG9uIHRoZSBUaXRhbmljIiwKICAgICAgIHggPSBOVUxMLAogICAgICAgeSA9ICJBdmVyYWdlIGRlY3JlYXNlIGluIHRoZSBHaW5pIEluZGV4IiwKICAgICAgIGNvbG9yID0gIk1ldGhvZCIpCmBgYAoKVGhlIE9PQiBlcnJvciByYXRlIGlzIGEgY291cGxlIHBvaW50cyBzbWFsbGVyIG9uIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsLCBhbmQgaW4gdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgZ2VuZGVyIGlzIG5vIGxvbmdlciBleGNsdXNpdmVseSB1c2VkIHRvIGdlbmVyYXRlIHRoZSBmaXJzdCBzcGxpdCBmb3IgZWFjaCB0cmVlLiBXZSBjYW4gYWxzbyBvYnNlcnZlIHRoYXQgdGhlIGF2ZXJhZ2UgZGVjcmVhc2UgaW4gdGhlIEdpbmkgaW5kZXggYXNzb2NpYXRlZCB3aXRoIGVhY2ggdmFyaWFibGUgaXMgZ2VuZXJhbGx5IHNtYWxsZXIgdXNpbmcgdGhlIHJhbmRvbSBmb3Jlc3QgbWV0aG9kIGNvbXBhcmVkIHRvIGJhZ2dpbmcgLSB0aGlzIGlzIGJlY2F1c2Ugb2YgdGhlIHZhcmlhYmxlIHJlc3RyaWN0aW9uIGltcG9zZWQgd2hlbiBjb25zaWRlcmluZyBzcGxpdHMuCgojIEJvb3N0aW5nCgoqKkJvb3N0aW5nKiogaXMgYW5vdGhlciBhcHByb2FjaCB0byBpbXByb3ZlIHVwb24gdGhlIHJlc3VsdCBvZiBhIHNpbmdsZSBkZWNpc2lvbiB0cmVlLiBJbnN0ZWFkIG9mIGNyZWF0aW5nIG11bHRpcGxlIGluZGVwZW5kZW50IGRlY2lzaW9uIHRyZWVzIHRocm91Z2ggYSBib290c3RyYXBwaW5nIHByb2Nlc3MsIGJvb3N0aW5nIGdyb3dzIHRyZWVzICoqc2VxdWVudGlhbGx5KiosIHVzaW5nIGluZm9ybWF0aW9uIGZyb20gdGhlIHByZXZpb3VzbHkgZ3Jvd24gdHJlZXMuIFJhdGhlciB0aGFuIGZpdHRpbmcgYSBtb2RlbCB0byB0aGUgcmVzcG9uc2UgdmFyaWFibGUgJFkkLCB3ZSBmaXQgYSBsYXJnZSBudW1iZXIgb2YgZGVjaXNpb24gdHJlZXMgJFxoYXR7Zn1eMSwgXGRvdHMsIFxoYXR7Zn1eQiQgdG8gdGhlIGN1cnJlbnQgKipyZXNpZHVhbHMqKi4gRWFjaCB0aW1lIGEgbmV3IGRlY2lzaW9uIHRyZWUgaXMgZXN0aW1hdGVkLCB0aGUgcmVzaWR1YWxzIGFyZSB1cGRhdGVkIGNvbWJpbmluZyB0aGUgcmVzdWx0cyBvZiBhbGwgcHJldmlvdXMgZGVjaXNpb24gdHJlZXMgaW4gcHJlcGFyYXRpb24gZm9yIGZpdHRpbmcgdGhlIG5leHQgdHJlZS4KClJhdGhlciB0aGFuIGxlYXJuaW5nIGhhcmQgYW5kIGZhc3QgbGlrZSBpbiBiYWdnaW5nIGFuZCByYW5kb20gZm9yZXN0cywgYm9vc3RpbmcgKipsZWFybnMgc2xvd2x5Kiogb3ZlciB0aW1lIGFzIG5ldyB0cmVlcyBhcmUgYWRkZWQuIEJlY2F1c2UgYm9vc3RpbmcgaXMgYWRkaXRpdmUgYW5kIHNsb3csIHdlIGNhbiBlc3RpbWF0ZSBmYWlybHkgc21hbGwgdHJlZXMgYW5kIHN0aWxsIGdhaW4gY29uc2lkZXJhYmxlIHByZWRpY3RpdmUgcG93ZXIuCgpCb29zdGluZyBpcyBhIGdlbmVyYWwgcHJvY2VzcyB0aGF0IGNhbiBiZSB1c2VkIGZvciBvdGhlciBzdGF0aXN0aWNhbCBsZWFybmluZyBtZXRob2RzLiBUaGUgdGhyZWUgbWFpbiB0dW5pbmcgcGFyYW1ldGVycyB3aGVuIGJvb3N0aW5nIGFyZToKCjEuIFRoZSAqKm51bWJlciBvZiB0cmVlcyoqICRCJC4gSWYgJEIkIGlzIHRvbyBsYXJnZSwgYm9vc3RpbmcgY2FuIG92ZXJmaXQuIFR5cGljYWxseSB3ZSB3b3VsZCB1c2UgY3Jvc3MtdmFsaWRhdGlvbiB0byBzZWxlY3QgJEIkLgoxLiBUaGUgKipzaHJpbmthZ2UgcGFyYW1ldGVyKiogJFxsYW1iZGEkLCB3aGljaCBpcyBhIHNtYWxsIHBvc2l0aXZlIG51bWJlciAoaS5lLiAkLjAxJCBvciAkLjAwMSQpLiBUaGlzIGNvbnRyb2xzIHRoZSByYXRlIGF0IHdoaWNoIGJvb3N0aW5nIGxlYXJucy4gQXMgJFxsYW1iZGEkIGdldHMgc21hbGxlciwgJEIkIGdlbmVyYWxseSBtdXN0IGluY3JlYXNlLgoxLiBUaGUgKipudW1iZXIgb2YgJGQkIHNwbGl0IGluIGVhY2ggdHJlZSoqLiBTdXJwcmlzaW5nbHksICRkPTEkIGFjdHVhbGx5IHdvcmtzIHdlbGwgd2hpY2ggaXMgZXNzZW50aWFsbHkgYW4gYWRkaXRpdmUgbW9kZWwgKGVhY2ggdHJlZSBpcyBhICoqc3R1bXAqKiB3aXRoIGEgc2luZ2xlIHByZWRpY3RvciksIHRob3VnaCBsYXJnZXIgdmFsdWVzIG9mICRkJCBhcmUgYWxzbyBjb21tb24uCgpMZXQncyBldmFsdWF0ZSBhbGwgdGhlIGFwcHJvYWNoZXMgd2UndmUgc2VlbiBzbyBmYXIgdXNpbmcgdGhlIFRpdGFuaWMgbW9kZWwuCgpgYGB7ciB0aXRhbmljLWNvbXBhcmUtYWxsfQp0aXRhbmljX3NwbGl0IDwtIHJlc2FtcGxlX3BhcnRpdGlvbih0aXRhbmljX3JmX2RhdGEsIHAgPSBjKCJ0ZXN0IiA9IC4zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0cmFpbiIgPSAuNykpCgp0aXRhbmljX21vZGVscyA8LSBsaXN0KCJiYWdnaW5nIiA9IHJhbmRvbUZvcmVzdChTdXJ2aXZlZCB+IC4sIGRhdGEgPSB0aXRhbmljX3NwbGl0JHRyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gNywgbnRyZWUgPSAxMDAwMCksCiAgICAgICAgICAgICAgICAgICAgICAgInJmX210cnkyIiA9IHJhbmRvbUZvcmVzdChTdXJ2aXZlZCB+IC4sIGRhdGEgPSB0aXRhbmljX3NwbGl0JHRyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXRyeSA9IDIsIG50cmVlID0gMTAwMDApLAogICAgICAgICAgICAgICAgICAgICAgICJyZl9tdHJ5NCIgPSByYW5kb21Gb3Jlc3QoU3Vydml2ZWQgfiAuLCBkYXRhID0gdGl0YW5pY19zcGxpdCR0cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG10cnkgPSA0LCBudHJlZSA9IDEwMDAwKSwKICAgICAgICAgICAgICAgICAgICAgICAiYm9vc3RpbmdfZGVwdGgxIiA9IGdibShhcy5udW1lcmljKFN1cnZpdmVkKSAtIDEgfiAuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0aXRhbmljX3NwbGl0JHRyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAxMDAwMCwgaW50ZXJhY3Rpb24uZGVwdGggPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAiYm9vc3RpbmdfZGVwdGgyIiA9IGdibShhcy5udW1lcmljKFN1cnZpdmVkKSAtIDEgfiAuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0aXRhbmljX3NwbGl0JHRyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAxMDAwMCwgaW50ZXJhY3Rpb24uZGVwdGggPSAyKSwKICAgICAgICAgICAgICAgICAgICAgICAiYm9vc3RpbmdfZGVwdGg0IiA9IGdibShhcy5udW1lcmljKFN1cnZpdmVkKSAtIDEgfiAuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB0aXRhbmljX3NwbGl0JHRyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAxMDAwMCwgaW50ZXJhY3Rpb24uZGVwdGggPSA0KSkKCmJvb3N0X3Rlc3RfZXJyIDwtIGRhdGFfZnJhbWUoYmFnZ2luZyA9IHByZWRpY3QodGl0YW5pY19tb2RlbHMkYmFnZ2luZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gYXNfdGliYmxlKHRpdGFuaWNfc3BsaXQkdGVzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdC5hbGwgPSBUUlVFKVtbMl1dICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBwbHkoMiwgZnVuY3Rpb24oeCkgeCAhPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSRTdXJ2aXZlZCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBseSgyLCBtZWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZl9tdHJ5MiA9IHByZWRpY3QodGl0YW5pY19tb2RlbHMkcmZfbXRyeTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdC5hbGwgPSBUUlVFKVtbMl1dICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBwbHkoMiwgZnVuY3Rpb24oeCkgeCAhPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSRTdXJ2aXZlZCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBseSgyLCBtZWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZl9tdHJ5NCA9IHByZWRpY3QodGl0YW5pY19tb2RlbHMkcmZfbXRyeTQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdC5hbGwgPSBUUlVFKVtbMl1dICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXBwbHkoMiwgZnVuY3Rpb24oeCkgeCAhPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSRTdXJ2aXZlZCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBseSgyLCBtZWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib29zdGluZ19kZXB0aDEgPSBwcmVkaWN0KHRpdGFuaWNfbW9kZWxzJGJvb3N0aW5nX2RlcHRoMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAxOjEwMDAwKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFwcGx5KDIsIGZ1bmN0aW9uKHgpIHJvdW5kKHgpID09IGFzLm51bWVyaWMoYXNfdGliYmxlKHRpdGFuaWNfc3BsaXQkdGVzdCkkU3Vydml2ZWQpIC0gMSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBseSgyLCBtZWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib29zdGluZ19kZXB0aDIgPSBwcmVkaWN0KHRpdGFuaWNfbW9kZWxzJGJvb3N0aW5nX2RlcHRoMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAxOjEwMDAwKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFwcGx5KDIsIGZ1bmN0aW9uKHgpIHJvdW5kKHgpID09IGFzLm51bWVyaWMoYXNfdGliYmxlKHRpdGFuaWNfc3BsaXQkdGVzdCkkU3Vydml2ZWQpIC0gMSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBseSgyLCBtZWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib29zdGluZ19kZXB0aDQgPSBwcmVkaWN0KHRpdGFuaWNfbW9kZWxzJGJvb3N0aW5nX2RlcHRoNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBhc190aWJibGUodGl0YW5pY19zcGxpdCR0ZXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4udHJlZXMgPSAxOjEwMDAwKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFwcGx5KDIsIGZ1bmN0aW9uKHgpIHJvdW5kKHgpID09IGFzLm51bWVyaWMoYXNfdGliYmxlKHRpdGFuaWNfc3BsaXQkdGVzdCkkU3Vydml2ZWQpIC0gMSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcHBseSgyLCBtZWFuKSkKCmJvb3N0X3Rlc3RfZXJyICU+JQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSkgJT4lCiAgbXV0YXRlX2VhY2goZnVucyhjdW1tZWFuKC4pKSwgYmFnZ2luZzpyZl9tdHJ5NCkgJT4lCiAgZ2F0aGVyKG1vZGVsLCBlcnIsIC1pZCkgJT4lCiAgbXV0YXRlKG1vZGVsID0gZmFjdG9yKG1vZGVsLCBsZXZlbHMgPSBuYW1lcyh0aXRhbmljX21vZGVscyksCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkJhZ2dpbmciLCAiUmFuZG9tIGZvcmVzdDogbSA9IFxcc3FydChwKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJhbmRvbSBmb3Jlc3Q6IG0gPSA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQm9vc3Rpbmc6IGRlcHRoID0gMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJvb3N0aW5nOiBkZXB0aCA9IDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCb29zdGluZzogZGVwdGggPSA0IikpKSAlPiUKICBnZ3Bsb3QoYWVzKGlkLCBlcnIsIGNvbG9yID0gbW9kZWwpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIkRhcmsyIikgKwogIGxhYnMoeCA9ICJOdW1iZXIgb2YgdHJlZXMiLAogICAgICAgeSA9ICJUZXN0IGNsYXNzaWZpY2F0aW9uIGVycm9yIiwKICAgICAgIGNvbG9yID0gIk1vZGVsIikKYGBgCgpVc2luZyBiYWdnaW5nIG9yIHJhbmRvbSBmb3Jlc3QgbWV0aG9kcywgdGhlIG1vZGVscyBxdWlja2x5IGNvbnZlcmdlIG9uIGEgdGVzdCBjbGFzc2lmaWNhdGlvbiBlcnJvciByYXRlLiBUaGlzIGhlbHBzIHRvIGRlbW9uc3RyYXRlIHRoYXQgZm9yIGJhZ2dpbmcgYW5kIHJhbmRvbSBmb3Jlc3RzLCB5b3UgZG8gbm90IG5lZWQgYSBwYXJ0aWN1bGFybHkgbGFyZ2UgJEIkIHRvIGJ1aWxkIGEgZ29vZCBtb2RlbC4gRm9yIGJvb3N0aW5nLCBhZGRpdGlvbmFsIHRyZWVzIGFyZSBuZWNlc3NhcnkgZm9yIHRoZSBlcnJvciByYXRlIHRvIGJlZ2luIGNvbnZlcmdpbmcgYW5kIHN0YWJpbGl6aW5nIGFyb3VuZCBhIHNpbmdsZSB2YWx1ZS4gV2UgY2FuIHVzZSB0aGUgYGdibS5wZXJmKClgIGZ1bmN0aW9uIHRvIGhlbHAgZGV0ZXJtaW5lIHRoZSBvcHRpbWFsIG51bWJlciBvZiBib29zdGluZyBpdGVyYXRpb25zIGJhc2VkIG9uIGVpdGhlciBPT0IsIHRlc3Qgc2V0LCBvciBDViBlc3RpbWF0ZXMgb2YgdGhlIGVycm9yIHJhdGUvTVNFOgoKYGBge3IgdGl0YW5pYy1ib29zdC1vcHR9CmRhdGFfZnJhbWUoZGVwdGggPSBjKDEsIDIsIDQpLAogICAgICAgICAgIG1vZGVsID0gdGl0YW5pY19tb2RlbHNbYygiYm9vc3RpbmdfZGVwdGgxIiwgImJvb3N0aW5nX2RlcHRoMiIsICJib29zdGluZ19kZXB0aDQiKV0sCiAgICAgICAgICAgb3B0aW1hbCA9IG1hcF9kYmwobW9kZWwsIGdibS5wZXJmLCBwbG90Lml0ID0gRkFMU0UpKSAlPiUKICBzZWxlY3QoLW1vZGVsKSAlPiUKICBrbml0cjo6a2FibGUoY2FwdGlvbiA9ICJPcHRpbWFsIG51bWJlciBvZiBib29zdGluZyBpdGVyYXRpb25zIiwKICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygiRGVwdGgiLCAiT3B0aW1hbCBudW1iZXIgb2YgaXRlcmF0aW9ucyIpKQogIApgYGAKCiMgU2Vzc2lvbiBJbmZvIHsudG9jLWlnbm9yZX0KCmBgYHtyIGNoaWxkPSdfc2Vzc2lvbmluZm8uUm1kJ30KYGBgCgoKCgo=

This work is licensed under the CC BY-NC 4.0 Creative Commons License.